领域驱动模型一种设计思想,我们又称为DDD设计思想。是一种为了解决传统设计思想带来的维护困难,沟通困难和交互困难而产生的一种新的思想。
采用面向对象的设计方法,系统包括业务接入层、业务逻辑层和数据库层,采用经典的三层架构。
这种架构的问题:
这种架构容易使系统变得臃肿,可扩展性和弹性伸缩性差
- 贫血模型:只有get、set方法,所有的业务逻辑都不包含在内,而是放在service中
- 充血模型:数据和对应的业务逻辑被封装到同一个类中
随着微服务架构理念的提出,单体架构向分布式微服务架构演进。微服务的设计目的是为了让大型软件系统解耦。将不同职责的服务独立部署,从而实现服务内部高内聚,服务之间低耦合的效果,让开发变得更为灵活。
这种架构的问题:
微服务的拆分困难,微服务到底应该如何拆分和设计呢?微服务的边界应该在哪里?
我们可以利用 DDD 设计方法来建立领域模型,划分领域边界,再根据这些领域边界从业务视角来划分微服务边界。
领域驱动设计的最大价值是让我们告别从面向过程式的思想(天马星空,想到哪写到哪)转化为基于系统化的模型驱动思维。
领域:
DDD 的领域就是这个边界内要解决的业务问题域 。
领域往往就是业务的某一个部分 , 例如电商的销售部分、物流部分、供应链部分等, 这些对于电商来说就是各个领域(模块),领域主要作用就是用来驱动范围, DDD 会将问题范围限定在特定的边界内,在这个边界内建立领域模型,进而用代码实现该领域模型,解决相应的业务问题。
子域:相对的一个概念, 我们可以将领域进行进一步的划分 , 这时候就是子域, 甚至可以对子域继续划分形成 子子域(依旧叫子域)。
实体:
实体以 DO(领域对象)的形式存在,拥有唯一标识符,且标识符在历经各种状态变更后仍能保持一致(唯一标识)。实体类通常采用充血模型,与这个实体相关的所有业务逻辑都在实体类的方法中实现。简单来说就是有唯一标识+业务行为方法。
值对象:
实体相对应的就是值对象,如果没有唯一标识就是值对象。值对象一般是嵌套在实体里面的。嵌套方式可能有两种,一种是直接嵌套整个Java对象,二是嵌套一个json对象。其次值对象一般是不会改变的,变化的时候也是整体替换。值对象没有太多的业务行为更多的是对实体的一个修饰和描述,离开了实体就没有任何存在的业务意义
实体可以理解为一张数据库表,必须有主键。值对象没有主键,依附于实体而存在,比如用户实体下住址对象,一般在数据库中已 json 字符串的形式存在;最常见的值对象是枚举。
案例 1:以属性嵌入的方式形成的人员实体对象,地址值对象直接以属性值嵌入人员实体中。
案例 2:以序列化大对象的方式形成的人员实体对象,地址值对象被序列化成大对象 Json 串后,嵌入人员实体中。
聚合根
聚合本身也是一个实体,聚合可以包含其他实体,其他实体不能脱离聚合而单独提供服务,比如一篇文章下的评论,评论必须从属于文章,没有文章也就没有评论。仓库层(repository)也必须是以聚合为核心提供服务的。
聚合
聚合就是由业务和逻辑紧密关联的实体和值对象组合而成的,聚合是数据修改和持久化的基本单元,每一个聚合对应一个仓储,实现数据的持久化。
领域模型内的实体和值对象就好比个体,而能让实体和值对象协同工作的组织就是聚合,它用来确保这些领域对象在实现共同的业务逻辑时,能保证数据的一致性。
如果把聚合比作组织,那聚合根就是这个组织的负责人。聚合根也称为根实体,它不仅是实体,还是聚合的管理者。
用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。这个边界定义了模型的适用范围,使团队所有成员能够明确地知道什么应该在模型中实现,什么不应该在模型中实现。
正如电商领域的商品一样,商品在不同的阶段有不同的术语,在销售阶段是商品,而在运输阶段则变成了货物。同样的一个东西,由于业务领域的不同,赋予了这些术语不同的涵义和职责边界,这个边界就可能会成为未来微服务设计的边界。看到这,我想你应该非常清楚了,领域边界就是通过限界上下文来定义的
边界体现在以下几方面:
单个实体对象能处理的逻辑放到实体里,多个实体或有交互的场景放到领域服务里。
领域服务可不可以调用仓储层或外部接口? 可以,但不能直接和领域服务代码放一起,领域服务模块存放API,实现放基础层(infrastructure)。
领域服务对象不建议直接以聚合名+DomainService命名,而要以操作命令关联,比如用户保存服务命名为:UserSaveService, 审核服务:UserAuditSerivce。
可以使用领域服务的情况:
仓储又分为两部分:仓储接口和仓储实现。仓储接口放在领域层中,仓储实现放在基础层。原来三层架构通用的第三方工具包、驱动、Common、Utility、Config 等通用的公共的资源类统一放到了基础层。