代码的分层

2022-06-02 18:03:06 浏览数 (1)

看了一下seata的example springcloud-eureka-feign-mybatis-seata,看到一个自己项目中使用代码分层不合理的地方,所以总结一下应用分层的一些感想。

目前我们的项目的代码分层结构是使用Controller>>Service>>Dao>>Mapper>>DB 的数据访问方向,当然可能再会分一些package,再加上manager层,manager层参考阿里的:

我们因为用的Mybatis框架:所以使用内部的数据映射器(Data Mapper)模式,它分离了领域模型和数据库访问代码的细节,也封装了数据映射的细节。

我们的问题在于Mapper层和DAO层的代码一模一样,只是为了使用@Mapper这个注解吗?不管是叫 XXXDAO 还是 XXXMapper,都暗示了它们与数据库的关系。Mapper层可以废掉的:

代码语言:javascript复制
//结合seata-example springcloud-eureka-feign-mybatis-seata:
1.在application上加@MapperScan("io.seata.sample.dao")
2.@Repository public interface StockDao 
3.mapper.xml 中 <mapper namespace="io.seata.sample.dao.StockDao">

根据自己的一些经验,分享一下代码分层的一些注意事项:

拆分阶段

第一,校验、转换传入的数据;第二,根据传入或转换后的数据,完成业务处理;第三,准备要返回的数据并返回。其中第二个阶段如果过于复杂,还可以拆分成更多的小步骤。采用面向契约编程,Service层保护DAO层,所以DAO层可以不做参数校验。

输入和输出

输入参数和输出参数隔离,一般稍大的系统都是分层设计的,最底层是数据存储层,数据库,最上层是对外提供接口调用的应用层,那每一层都有关联的数据对象,所以需要做相应的区分。让类语义更明确,很容易知道类的含义。DO,DTO等POJO类时,不要设定任何属性默认值;不要使用基础类型,使用包装类型。

分清业务逻辑和应用逻辑

对于用户验证、日志记录、事务、性能等非功能需求,显然是应用逻辑,这个自不用说。

对于一些难以说清楚的逻辑,我是这么区分的(不一定正确,但你可以参考):对于传统行业来说,将原来的手动流程变为信息化流程的,都属于业务逻辑;而由信息化带来的增值服务(比如自动发短信通知),就属于应用逻辑,也就是软件系统给我们带来的那些逻辑。

DDD

如果你了解领域驱动设计(DDD),一定会相当熟悉应用服务、领域模型、仓库这些模式。但这些模式并不只属于 DDD。在 DDD 诞生之前,这些模式就已经存在了,《企业应用架构模式》中甚至还提出了很多可以替代的模式。DDD 只是把这些模式进行组合,形成了一套以领域模型模式为基础的最佳实践。

先看一个例子:

代码语言:javascript复制
public class UserRepository {
    public void add(User user) { } 
    public void save(User user) { } 
    public User findById(long userId) { }
    public boolean isBorrowedWith(long bookId) { }
}
//第一种
UserRepository.findById(1000L).isBorrowed()
//第二种
UserRepository.isBorrowedWith(1000L)

一旦你用了第二种,未来任何类似的方法都将加到Repository上,User很快就变回了贫血模型。第二种属于事务脚本模式。Repository要么操作领域模型,要么返回领域模型,想这种既不操作也不返回的,实际上是把Repository当Dao用了。

不要为了用而用DDD,不管代码的分层是不是按DDD的,最重要的是领域模型方式编程。

代码分层,适合自己的最佳实践才是最好的,但是要多学习借鉴。这么多优秀的编程范式,而且我们的智商远不如这些大神们,为什么不能拿来主义?


每周一句:推荐一本书——刘润的《底层逻辑》,类似一套思维框架,比如辨识能力-我们应该优先考虑的是"为什么"后面的观点是否成立,再去回答别人为什么。

0 人点赞