两个案例
项目代码分层
很多同学在代码结构分层上会参考
开放接口层:可直接封装 Service 接口暴露成 RPC 接口; 通过 Web 封装成 http 接口; 网关控
制层等。
终端显示层:各个端的模板渲染并执行显示层。 当前主要是 velocity 渲染, JS 渲染, JSP 渲
染,移动端展示层等。
Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
Service 层:相对具体的业务逻辑服务层。
Manager 层:通用业务处理层,它有如下特征:
1) 对第三方平台封装的层,预处理返回结果及转化异常信息;
2) 对 Service 层通用能力的下沉,如缓存方案、 中间件通用处理;
3) 与 DAO 层交互,对 DAO 的业务通用能力的封装。
DAO 层:数据访问层,与底层 MySQL、 Oracle、 Hbase 进行数据交互。
外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。
这里比较有争议的就是Manager 层与service 层的关系。Manager 层对service 能力的下层,如果我不想service层有相互调用或者在Manager 下层的能力有对service 层的依赖,很容易将破坏上面分层的内在逻辑,不管你怎么妥协,他不完美。更致命的事,service 在POM上有对manager 的dependency, manager 如何反客为主呢,maven 是无法循环依赖的。
版本冲突仲裁
默认解决依赖冲突:
第一原则:路径最近者优先
项目A有如下的依赖关系:
A.SERVICE->POM->PARENT->JSON(1.0)
A.SERVICE->DOMAIN–>JSON(2.0)
则该例子中,JSON的版本是2.0
第二原则:路径相等,先声明者优先
项目A有如下的依赖关系:
A.SERVICE->DOMAIN->JSON(1.0)
A.SERVICE->DAO->JSON(2.0)
若pom文件中DOMAIN中的依赖坐标先于DAO中进行声明,则最终JSON的版本为1.0
手动解决冲突
<dependency>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>project-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>project-b</artifactId>
</exclusion>
</exclusions>
</dependency>
手动依赖排除即可
无法解决的冲突
如果你的项目是安排第一个案例这样很棒的分层,那么恭喜你,你很有可能遇到以上两种方法无法解决的冲突。我曾经遇到这样的问题,折腾了两个小时才最终定位。
项目A有如下的依赖关系:
A.DAO(1.0)->POM->JSON(1.0)
A.SERVICE(2.0)->POM->JSON(2.0)
项目A 中最终使用JSON的版本是?
答案是版本2.0
最佳实践
案例一的最佳实践
将service 层分拆接口层和实现层。这种情况下,我们解决了循环依赖的同时,可以给予manager 层更多的空间和功能定义。
案例二的最佳实践
在 A.SERVICE/A.DAO->POM ,在POM中对JSON的版本进行声明(dependencyManagerment)。分层后的各模块如果是同机部署,这时依赖冲突的范围会扩大至整个项目,这个时候除了关心依赖链的冲突外,还需关心加载链的冲突(个人发明,大家理解下)。
瞎掰工具与架构
maven 是管理包依赖的工具,架构是组织代码形式和抽象的方法,两者看似很远,当我们在架构时充分考虑到12 因素,对于实施中的依赖和构建给予更多的注意力,这时maven 跟架构的关系比想象中近。