初次接触DDD设计,听完培训课后,满是疑惑却又不知从何问起,回想后,大抵是自己接触的项目规模太小,不能够充分理解其中的奥妙。便于课后去听了他人的讲解,翻阅了别人的笔记,记下了零散的,自己目前能够浅浅理解的内容。
初次感悟:DDD关注业务需求,站在一个更为高而广的角度去思考分析问题,领域这个词真的是太合适不过了,将每一个业务需求的实现,去细化为更为严谨不可分的单元,形成一个属于自己的领域,做到了在功能实现上互为联系,但在后期维护上互不干扰。
1.DDD全称为(Domain-Driven Design,简称DDD),领域驱动设计
领域驱动设计的核心是建立正确的领域模型。
领域模型思考问题的角度:
以Eric Evans(DDD之父)在他的书中的一个货物运输系统为例子简单说明一下。在经过一些用户需求讨论之后,在用户需求相对明朗之后,Eric这样描述领域模型:
1.一个Cargo(货物)涉及多个Customer(客户,如托运人、收货人、付款人),每个Customer承担不同的角色;
2.Cargo的运送目标已指定,即Cargo有一个运送目标;
3.由一系列满足Specification(规格)的Carrier Movement(运输动作)来完成运输目标; 从上面的描述我们可以看出,他完全没有从用户的角度去描述领域模型,而是以领域内的相关事物为出发点,考虑这些事物的本质关联及其变化规律的。上述这段描述完全以货物为中心,把客户看成是货物在某个场景中可能会涉及到的关联角色,如货物会涉及到托运人、收货人、付款人;货物有一个确定的目标,货物会经过一系列的运输动作到达目的地;其实,我觉得以用户为中心来思考领域模型的思维只是停留在需求的表面,而没有挖掘出真正的需求的本质;我们在做领域建模时需要努力挖掘用户需求的本质,这样才能真正实现用户需求;
2.领域驱动设计的经典分层架构
2.1.用户界面/展现层
负责向用户展现信息以及解释用户命令。更细的方面来讲就是:
1. 请求应用层以获取用户所需要展现的数据;
2. 发送命令给应用层要求其执行某个用户命令;
2.2.应用层
很薄的一层,定义软件要完成的所有任务。对外为展现层提供各种应用功能(包括查询或命令),对内调用领域层(领域对象或领域服务)完成各种业务逻辑,应用层不包含业务逻辑。
2.3.领域层
负责表达业务概念,业务状态信息以及业务规则,领域模型处于这一层,是业务软件的核心。
2.4.基础设施层(infracore)
编写整个项目中全部通用的部分,例如:数据库操作,配置类,工具类and so on.
本层为其他层提供通用的技术能力;提供了层间的通信;为领域层实现持久化机制;总之,基础设施层可以通过架构和框架来支持其他层的技术需求;
DDD四层架构和三层架构的对比图
DDD分层架构代码目录结构
3.DDD中的一些概念理解
3.1.实体(Entity)
实体就是领域中需要唯一标识的领域概念。因为我们有时需要区分是哪个实体。有两个实体,如果唯一标识不一样,那么即便实体的其他所有属性都一样,我们也认为他们两个不同的实体;因为实体有生命周期,实体从被创建后可能会被持久化到数据库,然后某个时候又会被取出来。所以,如果我们不为实体定义一种可以唯一区分的标识,那我们就无法区分到底是这个实体还是哪个实体。另外,不应该给实体定义太多的属性或行为,而应该寻找关联,发现其他一些实体或值对象,将属性或行为转移到其他关联的实体或值对象上。比如Customer实体,他有一些地址信息,由于地址信息是一个完整的有业务含义的概念,所以,我们可以定义一个Address对象,然后把Customer的地址相关的信息转移到Address对象上。如果没有Address对象,而把这些地址信息直接放在Customer对象上,并且如果对于一些其他的类似Address的信息也都直接放在Customer上,会导致Customer对象很混乱,结构不清晰,最终导致它难以维护和理解;
3.2.值对象(Value Object)
在领域中,并不是每一个事物都必须有一个唯一标识,也就是说我们不关心对象是哪个,而只关心对象是什么。就以上面的地址对象Address为例,如果有两个Customer的地址信息是一样的,我们就会认为这两个Customer的地址是同一个。也就是说只要地址信息一样,我们就认为是同一个地址。用程序的方式来表达就是,如果两个对象的所有的属性的值都相同我们会认为它们是同一个对象的话,那么我们就可以把这种对象设计为值对象。因此,值对象没有唯一标识,这是它和实体的最大不同。另外值对象在判断是否是同一个对象时是通过它们的所有属性是否相同,如果相同则认为是同一个值对象;而我们在区分是否是同一个实体时,只看实体的唯一标识是否相同,而不管实体的属性是否相同;值对象另外一个明显的特征是不可变,即所有属性都是只读的。因为属性是只读的,所以可以被安全的共享;当共享值对象时,一般有复制和共享两种做法,具体采用哪种做法还要根据实际情况而定;另外,我们应该给值对象设计的尽量简单,不要让它引用很多其他的对象,因为他只是一个值,就像int a = 3;那么”3”就是一个我们传统意义上所说的值,而值对象其实也可以和这里的”3”一样理解,也是一个值,只不过是用对象来表示。
3.3.聚合及聚合根(Aggregate,Aggregate Root)
聚合,它通过定义对象之间清晰的所属关系和边界来实现领域模型的内聚,并避免了错综复杂的难以维护的对象关系网的形成。聚合定义了一组具有内聚关系的相关对象的集合,我们把聚合看作是一个修改数据的单元。
如果一个聚合只有一个实体,那么这个实体就是聚合根;如果有多个实体,那么我们可以思考聚合内哪个对象有独立存在的意义并且可以和外部直接进行交互。
4.附上一张详细的图片