作者回复: 等我有时间的时候准备一下哈。现在的代码都是到类和方法级。
作者回复: 是Mybatis的映射文件。
关于仓储,我是这么考虑的。仓储本身是属于基础层,但是考虑到一个聚合对应一个仓储,为了以后聚合代码整体迁移的方便,我在微服务代码目录设计时,在聚合目录下增加了一个Repository的仓储目录,跟仓储相关的代码都在这个目录下。
这个目录下的代码与聚合的其它业务代码是分开的。如果未来换数据库的话,只需要将Repository目录下的代码替换就可以了。而如果聚合需要整体迁移到其它微服务中去,仓储的代码也会一并迁移。
作者回复: 依赖倒置举个例子,领域层是通过仓储接口获取基础资源的数据对象,仓储接口会调用仓储实现,具体的基础资源的数据处理过程是在仓储实现中完成的。这样做的好处是,避免将仓储实现的代码混入上层业务逻辑中。如果以后替换数据库,由于做了基础资源的个性的代码隔离,所以实现了应用逻辑与基础资源的解耦。在更换数据库时只需要更换仓储相关的代码就可以了,应用的逻辑不会受太大的影响。
防腐层我感觉主要是实现新旧系统切换时,出现业务逻辑混杂在一起的情况,避免污染领域模型的实现逻辑。因此增加防腐层隔离旧系统对领域模型的影响。在完成新旧切换后,防腐层的代码就可以抛弃不用了。
作者回复: 领域对象的概念比较广泛,除了实体、值对象和聚合根外,服务也算是领域对象。领域层和应用分别有领域服务和应用服务。
作者回复:
第一,可以这么理解。
第二,从本质来讲,DTO与VO都是对象。但是在DDD中将值传递的界限划分更细,比如DTO、DO、VO、PO,分别对应不同阶段的事务处理。DTO通常面向接口层,与VO相比可能会有前端应用/接口请求方要求的一些个性化的属性或值的映射等。
第三,简单部分可能会有重叠,但是基于不对外暴露领域层逻辑的目的,会将实体方法封装成领域服务,领域服务再封装为应用服务,然后对外暴露。
第四,这层本身是公共类,表示部分持久化的功能被提取为公共的面向切面聚合方法来实现
第五,是这样的。实体值对象的数据逻辑通过聚合根来管理,多实体的业务行为通过领域服务来组合。
作者回复: 用户接口层也很重要啊,主要前后端调用的适配。如果你的微服务要面向很多的应用或渠道提供服务,而每个渠道的入参和出参都不一样,你不太可能开发出太多的应用服务,这样Facade接口就起很好的作用了,包括DO和DTO对象的组装和转换等。
作者回复: 不好意思哈,没看太懂,能否举个例子说明一下。
作者回复: 给你看一个非常简单的例子,有Person聚合根,Person聚合包括仓储接口和仓储实现。
通过增加仓储服务,使得应用逻辑和数据库逻辑的依赖关系剥离,当换数据库的时候,只需要将仓储实现替换就可以了,这样不会对核心的业务逻辑产生影响。
/**
* Person聚合根
*/
public class Person{
private String id;
private String name;
private int age;
private boolean gender;
/**
* 其它方法
*/
}
/**
* Person仓储接口
*/
public interface PersonRepositoryInterface {
void save(Person person);
void delete(String id);
}
/**
*Person仓储实现
*/
@Repository
public class PersonRepositoryImp implements PersonRepositoryInterface {
private PersonMapper mapper;
public void save( Person person) {
mapper.create(person);
}
public void delete((String id) {
mapper.delete(id);
}
}
在应用逻辑中直接用仓储的接口就可以了,数据库相关的逻辑在PersonMapper里面实现。
PersonRepositoryInterface personRepos;
personRepos.save(person)
作者回复: 一般都是通过应用服务或者领域服务来完成仓储调用,实体或聚合根作为参数传入仓储接口,通过仓储实现来完成持久化。一个简单的例子如下:
public class OrderService {
private OrderRepository orderRepository;
public Order updateOrder(order) {
***;
orderRepository.save(order);
***;
}
}
作者回复: 我们先从底下往上逐层讲,单个实体自身的方法就是实体本身的业务行为。多个实体可组成更复杂的业务动作,这个是领域服务,实体的方法和领域服务共同构成领域模型的基础业务能力,这个能力是原子的基础的,不太考虑外界的用户行为和流程。而应用服务是对这些基础的能力进行组合和编排,它组合和编排的服务可以是跨聚合的领域服务,主要体现组合后的业务能力,更面向前端的用户操作,属于比较粗粒度的服务,通过编排可以更灵活应对外部需求变化。
作者回复: 1、差异很大的,三层架构没有领域模型的概念,业务逻辑混杂在一起。DAO也是没有严格区分应用逻辑和基础资源逻辑的。
2、是的。
3、在目录结构里,仓储接口和实现都在聚合目录里面。
4、这四层都是在一个微服务工程里面。
作者回复: 基础层的逻辑主要是SQL代码,DO和PO的转换和映射以及数据的持久化等。在没有仓储之前,这些代码会跟业务逻辑混杂在一起的。
领域层和应用层通过仓储接口会将DO的对象传给仓储实现,在仓储实现里面实现DO和PO的转换以及数据的持久化。
初始化的时候,仓储实现会实现PO到DO的转换,然后返回DO对象。
这样的话,业务逻辑都是基于DO的操作,不管基础层如何变化,只要DO不发生变化,业务逻辑都不会影响。这样就实现了业务逻辑与基础逻辑的解耦了。
仓储给你看一段在答疑那一节里面代码。
有Person聚合根,Person仓储接口和仓储实现。
/**
* Person聚合根
*/
public class Person{
private String id;
private String name;
private int age;
private boolean gender;
}
/**
* Person仓储接口
*/
public interface PersonRepositoryInterface {
void save(Person person);
void delete(String id);
}
/**
*Person仓储实现
*/
@Repository
public class PersonRepositoryImp implements PersonRepositoryInterface {
private PersonMapper mapper;
public void save( Person person) {
mapper.create(person);
}
public void delete((String id) {
mapper.delete(id);
}
}
在应用逻辑中直接用仓储的接口就可以了,数据库相关的逻辑在PersonMapper里面实现。
PersonRepositoryInterface personRepos;
personRepos.save(person)
作者回复: 应用层。
作者回复: 我主要分析的是四层😄。五层只是告诉大家有这个东西。后面也是基于四层来开展的。
作者回复: 尽量解耦吧。如果不存在业务逻辑与基础服务耦合的问题,感觉不用也可以。考虑一下综合成本和性价比。
作者回复: DO转PO。
作者回复: 实体除了自身相关的增删改查外,自身相关的其它业务行为也是在实体类内部来实现的,比如实体内不同属性之间的业务逻辑处理,多个同类实体的逻辑处理。
聚合根内对多个实体操作的方法,主要还是在数据方面的处理,本质上是实体的方法,只不过它是聚合根。
我理解的领域服务是对多个实体属性以及方法进行组合的业务处理,主要表现为多个实体组合出来的一段复杂业务逻辑。
作者回复: 能做到依赖分离,就不必要了。
作者回复: 是的,应用层的应用服务会调用其它微服务发布到API网关的服务。