DDD分层架构
DDD分层架构采用四层架构,从上到下依次是:用户接口层、应用层、领域层和基础层。
DDD分层架构有一个重要的原则:每层只能与位于其下方的层发生耦合。
根据耦合的紧密程度又分为两种:严格分层架构和松散分层架构。严格分层架构任何层只能对位于其直接下方的层产生依赖,松散分层架构允许某层与其任意下方的层发生依赖。
为了服务的可管理,建议采用严格分层架构。服务逐层对外封装或组合,依赖关系清晰。
/DDD分层架构.jpeg)
用户接口层
用户接口层负责向用户显示信息和解释用户指令。面向前端提供服务适配,面向资源层提供资源适配。这里的用户可能是:用户、程序、自动化测试和批处理脚本等。
应用层
应用层是很薄的一次,理论上不应该有业务规则或逻辑,主要面向用例和流程相关的操作。它可以协调多个聚合的服务和领域对象完成服务的编排和组合,协作完成业务操作。此外,应用层也是微服务之间交互的通道,它可以调用其他微服务的应用服务,完成微服务之间的服务组合和编排。对外提供粗粒度的服务。还有,应用服务还可以进行安全验证、权限校验、事务控制、发送或订阅领域事件等。
领域层
领域层的作用是实现企业核心业务逻辑,通过各种检验手段保证业务的正确性。领域层主要体现领域模型的业务能力,它用来表达业务概念、业务状态和业务规则。
领域层包含聚合根、实体、值对象、领域服务等领域模型中的领域对象。
基础层
基础层贯穿所有层,它的作用就是为其他各层提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。
注:不要把与领域无关的逻辑放在领域层实现,保证领域层的纯洁和领域逻辑的稳定,避免污染领域模型,也不要把领域模型的业务逻辑放在应用层,这样会导致应用层过于庞大,最终领域模型会失焦。
/服务调用.png)
边界
在演进式架构中,为适应业务的发展和需求的变更,微服务会不断的拆分或者重组为新的微服务。为了支持架构长期、轻松的演进,边界就显得尤为重要。
用 DDD 方法设计的微服务中,限界上下文和聚合就是用来定义领域模型和微服务边界的。它不仅可以实现微服务内外的解耦,同时也可以很容易地实现业务功能积木式模块的重组和更新,从而实现架构的演进。
为了方便理解,我们将这些边界分为:逻辑边界、物理边界和代码边界。
逻辑边界
微服务内聚合之间的边界为逻辑边界。它是一个虚拟的边界,强调业务的内聚,可以根据需要变成物理边界,也就是说聚合也可以独立为微服务。
物理边界
微服务之间的边界为物理边界。它强调微服务部署和运行的隔离,关注微服务的服务调用、容错和运行等。
代码边界
不同层或者聚合之间代码目录的边界为代码边界。它强调的是代码之间的隔离,方便架构演进式代码的重组。
通过以上边界,可以让业务能力高内聚、代码松耦合,且清晰的边界,可以快速实现微服务代码的拆分和组合,轻松实现微服务架构的演进。
数据对象视图
在 DDD 中有很多的数据对象,这些对象分布在不同的层里。它们在不同的阶段有不同的形态。
数据持久化对象
数据持久化对象 PO(Persistent Object),与数据库结构一一映射,是数据持久化过程中的数据载体。
领域对象
领域对象 DO(Domain Object),微服务运行时的实体,是核心业务的载体。
数据传输对象
数据传输对象 DTO(Data Transfer Object),用于前端与应用层或者微服务之间的数据组装和传输,是应用之间数据传输的载体。
视图对象
视图对象 VO(View Object),用于封装展示层指定页面或组件的数据。
微服务各层数据对象的职责和转换过程。/数据对象转换过程.png)
微服务代码模型
建立标准的微服务代码模型和代码规范,将领域对象所对应的代码对象放在合适的软件包的目录结构中。标准的代码模型可以让项目团队成员更好地理解代码,根据代码规范实现团队协作。还可以让微服务各层的逻辑互不干扰、分工协作、各居其位、各司其职,避免不必要的代码混淆。
其实,DDD 并没有给出标准的代码模型,下面的微服务代码模型主要考虑微服务的边界、分层以及架构演进。经过实践后建立起来的。
微服务一级目录结构
微服务一级目录是按照 DDD 分层架构的分层职责来定义的。在代码模型里分别为用户接口层、应用层、领域层和基础层,建立 interfaces、application、domain 和 infrastructure 四个一级代码目录。
/微服务代码一级目录.jpeg)
各层目录结构
用户接口层
interfaces 的代码目录结构有:assembler、dto 和 facade 三类。/interfaces.jpeg)
- assembler: 实现 DTO 与领域对象之间的相互转换和数据交换。一般来说 assembler 与 DTO 总是一同出现。
- dto: 它是数据传输的载体,内部不存在任何业务逻辑,我们可以通过 DTO 把内部的领域对象与外界隔离。
- facade: 提供较粗粒度的调用接口,将用户请求委派给一个或多个应用服务进行处理。
应用层
application 的代码目录结构有:event 和 service。/application.jpeg)
- event: 这层主要存放事件相关的代码。它包括两个子目录:publish 和 subscribe。(事件处理相关的核心业务逻辑在领域层实现)。
- service: 这层的服务是应用服务。应用服务会对多个领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度的服务。
领域层
domain 是由一个或多个聚合包构成。共同实现领域模型的核心业务逻辑。聚合内的代码模型是标准和统一的,包括:entity、event、repository 和 service 四个子目录。/domain.jpeg)
- aggregate: 它是聚合软件包的根目录,可以根据实际项目的聚合命名。在聚合内定义聚合根、实体和值对象以及领域服务之间的关系和边界。聚合内实现高聚合的业务逻辑,它的代码可以独立拆分为微服务。
- entity: 它存放聚合根、实体、值对象以及工厂模式(Factory)相关的代码。
- event: 它存放事件实体以及与事件活动相关的业务逻辑代码。
- service: 它存放领域服务代码。一个领域服务是多个实体组合出来的一段业务逻辑。领域服务封装多个实体或方法后向上层提供应用服务调用。
- repository: 它存放所在聚合的查询或持久化领域对象的代码,为了方便聚合的拆分和组合,我们设定了一个原则:一个聚合对应一个仓储。
基础层
infrastructure 的代码目录结构有:config 和 util 两个子目录。/infrastructure.jpeg)
- config: 主要存放配置相关的代码。
- util: 主要存放平台、开发框架、消息、数据库、缓存、文件、总线、网关、第三方类库、通用算法等基础代码。可以为不同的资源类别建立不同的子目录。
在完成一级和二级代码模型设计后,就可以看到下图这样的微服务代码模型的总目录结构了。/微服务代码总目录.jpeg)