DDD

DDD 分层架构与微服务代码模型

通过领域驱动设计可以很有效的指导微服务的拆分,设计和实现. 通过合理的拆分微服务,分层结构可以有效的实现系统复杂性的治理,构建高内聚,低耦合,可演进的分布式系统.

用户接口层:面向前端提供服务适配,面向资源层提供资源适配。这一层聚集了接口适 配相关的功能。

应用层职责:实现服务组合和编排,适应业务流程快速变化的需求。这一层聚集了应用 服务和事件相关的功能。

领域层:实现领域的核心业务逻辑。这一层聚集了领域模型的聚合、聚合根、实体、值 对象、领域服务和事件等领域对象,以及它们组合所形成的业务能力。

基础层:贯穿所有层,为各层提供基础资源服务。这一层聚集了各种底层资源相关的服 务和能力。

[图片上传失败...(image-2d1270-1627378304899)]

微服务代码模型

微服务一级目录结构

微服务一级目录是按照 DDD 分层架构的分层职责来定义的。从下面这张图中,我们可以看 到,在代码模型里分别为用户接口层、应用层、领域层和基础层,建立了 interfaces、 application、domain 和 infrastructure 四个一级代码目录。

各层目录结构

  1. 用户接口层

    Interfaces 的代码目录结构有:assembler、dto 和 façade 三类。

    Assembler:实现 DTO 与领域对象之间的相互转换和数据交换。一般来说 Assembler 与 DTO 总是一同出现。

    Dto:它是数据传输的载体,内部不存在任何业务逻辑,我们可以通过 DTO 把内部的领域 对象与外界隔离。

    Facade:提供较粗粒度的调用接口,将用户请求委派给一个或多个应用服务进行处理。

  2. 应用层

    Application 的代码目录结构有:event 和 service。

    Event(事件):这层目录主要存放事件相关的代码。它包括两个子目录:publish 和 subscribe。前者主要存放事件发布相关代码,后者主要存放事件订阅相关代码(事件处理 相关的核心业务逻辑在领域层实现)。

    Service(应用服务):这层的服务是应用服务。应用服务会对多个领域服务或外部应用服 务进行封装、编排和组合,对外提供粗粒度的服务。应用服务主要实现服务组合和编排,是 一段独立的业务逻辑。你可以将所有应用服务放在一个应用服务类里,也可以把一个应用服 务设计为一个应用服务类,以防应用服务类代码量过大。

  3. 领域层

    Domain 是由一个或多个聚合包构成,共同实现领域模型的核心业务逻辑。聚合内的代码 模型是标准和统一的,包括:entity、event、repository 和 service 四个子目录。

    Aggregate(聚合):它是聚合软件包的根目录,可以根据实际项目的聚合名称命名,比 如权限聚合。在聚合内定义聚合根、实体和值对象以及领域服务之间的关系和边界。聚合内 实现高内聚的业务逻辑,它的代码可以独立拆分为微服务。

    Entity(实体):它存放聚合根、实体、值对象以及工厂模式(Factory)相关代码。实体 类采用充血模型,同一实体相关的业务逻辑都在实体类代码中实现。跨实体的业务逻辑代码 在领域服务中实现。

    Entity(实体):它存放聚合根、实体、值对象以及工厂模式(Factory)相关代码。实体 类采用充血模型,同一实体相关的业务逻辑都在实体类代码中实现。跨实体的业务逻辑代码 在领域服务中实现。

    Service(领域服务):它存放领域服务代码。一个领域服务是多个实体组合出来的一段业 务逻辑。你可以将聚合内所有领域服务都放在一个领域服务类中,你也可以把每一个领域服 务设计为一个类。如果领域服务内的业务逻辑相对复杂,我建议你将一个领域服务设计为一 个领域服务类,避免由于所有领域服务代码都放在一个领域服务类中,而出现代码臃肿的问 题。领域服务封装多个实体或方法后向上层提供应用服务调用。

    Repository(仓储):它存放所在聚合的查询或持久化领域对象的代码,通常包括仓储接 口和仓储实现方法。为了方便聚合的拆分和组合,我们设定了一个原则:一个聚合对应一个 仓储。

  4. 基础层

    Infrastructure 的代码目录结构有:config 和 util 两个子目录。

Config:主要存放配置相关代码。

Util:主要存放平台、开发框架、消息、数据库、缓存、文件、总线、网关、第三方类库、 通用算法等基础代码,你可以为不同的资源类别建立不同的子目录。

从领域模型到微服务的设计

领域层的领域对象

  1. 设计实体

    大多数情况下,领域模型的业务实体与微服务的数据库实体是一一对应的。但某些领域模型 的实体在微服务设计时,可能会被设计为多个数据实体,或者实体的某些属性被设计为值对 象。

    我们分析个人客户时,还需要有地址、电话和银行账号等实体,它们被聚合根引用,不容易 在领域建模时发现,我们需要在微服务设计过程中识别和设计出来。

    在分层架构里,实体采用充血模型,在实体类内实现实体的全部业务逻辑。这些不同的实体 都有自己的方法和业务行为,比如地址实体有新增和修改地址的方法,银行账号实体有新增 和修改银行账号的方法。

    实体类放在领域层的 Entity 目录结构下。

  2. 找出聚合根

    聚合根来源于领域模型,在个人客户聚合里,个人客户这个实体是聚合根,它负责管理地 址、电话以及银行账号的生命周期。个人客户聚合根通过工厂和仓储模式,实现聚合内地 址、银行账号等实体和值对象数据的初始化和持久化。

    聚合根来源于领域模型,在个人客户聚合里,个人客户这个实体是聚合根,它负责管理地 址、电话以及银行账号的生命周期。个人客户聚合根通过工厂和仓储模式,实现聚合内地 址、银行账号等实体和值对象数据的初始化和持久化。

  3. 设计值对象

    根据需要将某些实体的某些属性或属性集设计为值对象。值对象类放在代码模型的 Entity 目录结构下。在个人客户聚合中,客户拥有客户证件类型,它是以枚举值的形式存在,所以 将它设计为值对象。

    有些领域对象可以设计为值对象,也可以设计为实体,我们需要根据具体情况来分析。如果 这个领域对象在其它聚合内维护生命周期,且在它依附的实体对象中只允许整体替换,我们 就可以将它设计为值对象。如果这个对象是多条且需要基于它做查询统计,我建议将它设计 为实体。

  4. 设计领域事件

    如果领域模型中领域事件会触发下一步的业务操作,我们就需要设计领域事件。首先确定领 域事件发生在微服务内还是微服务之间。然后设计事件实体对象,事件的发布和订阅机制, 以及事件的处理机制。判断是否需要引入事件总线或消息中间件。 在个人客户聚合中有客户已创建的领域事件,因此它有客户创建事件这个实体。 领域事件实体和处理类放在领域层的 Event 目录结构下。领域事件的发布和订阅类我建议 放在应用层的 Event 目录结构下。

  5. 设计领域服务

    如果一个业务动作或行为跨多个实体,我们就需要设计领域服务。领域服务通过对多个实体 和实体方法进行组合,完成核心业务逻辑。你可以认为领域服务是位于实体方法之上和应用 服务之下的一层业务逻辑。

    按照严格分层架构层的依赖关系,如果实体的方法需要暴露给应用层,它需要封装成领域服 务后才可以被应用服务调用。所以如果有的实体方法需要被前端应用调用,我们会将它封装成领域服务,然后再封装为应用服务。

    个人客户聚合根这个实体创建个人客户信息的方法,被封装为创建个人客户信息领域服务。 然后再被封装为创建个人客户信息应用服务,向前端应用暴露。

    领域服务类放在领域层的 Service 目录结构下。

  6. 设计仓储

    每一个聚合都有一个仓储,仓储主要用来完成数据查询和持久化操作。仓储包括仓储的接口 和仓储实现,通过依赖倒置实现应用业务逻辑与数据库资源逻辑的解耦。

    仓储代码放在领域层的 Repository 目录结构下。

服务的协作

  1. 服务的类型

    Facade 服务:位于用户接口层,包括接口和实现两部分。用于处理用户发送的 Restful 请 求和解析用户输入的配置文件等,并将数据传递给应用层。或者在获取到应用层数据后,将 DO 组装成 DTO,将数据传输到前端应用。

    应用服务:位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处 理业务用例的执行顺序以及结果拼装,对外提供粗粒度的服务。

    领域服务:位于领域层。领域服务封装核心的业务逻辑,实现需要多个实体协作的核心领域 逻辑。它对多个实体或方法的业务逻辑进行组合或编排,或者在严格分层架构中对实体方法 进行封装,以领域服务的方式供应用层调用。

    基础服务:位于基础层。提供基础资源服务(比如数据库、缓存等),实现各层的解耦,降 低外部资源变化对业务应用逻辑的影响。基础服务主要为仓储服务,通过依赖倒置提供基础 资源服务。领域服务和应用服务都可以调用仓储服务接口,通过仓储服务实现数据持久化。

  2. 服务的调用

    微服务的服务调用包括三类主要场景:微服务内跨层服务调用,微服务之间服务调用和领域事件驱动

你可能感兴趣的:(DDD)