5.微服务代码模型

1.微服务代码模型

代码分层

在微服务代码模型里,我们分别定义了用户接口层、并分别为它们建立了interfaces、application、domain和infrastructure四个一级代码目录;

  • interfaces(用户接口层): 它主要存放用户接口层与前端应用交互、数据转换和交互相关的代码;
  • application(应用层): 它主要存放与应用层服务组合和编排相关的代码。应用服务和事件等代码会放在这一层目录里;
  • domain(领域层): 它主要存放与领域层核心业务逻辑相关的代码。聚合内的聚合根以及实体、方法、值对象、领域服务和事件等相关代码会放在这一层目录里;
  • infrastructure(基础层): 它主要存放与基础资源服务相关的代码;

代码目录

  • 用户接口层有assembler、 dto和facade三类;
  • 应用层有event和service。为每个聚合的应用服务设计一个应用服务类;
    对于多表关联的复杂查询,由于这种复杂查询不需要有领域逻辑和业务规则约束,因此不建议将这类复杂查询放在领域层的领域模型中,可以通过应用层的应用服务采用传统多表关联的SQL查询方式;
  • 领域层有entity、event、 repository和service四个子目录;
    domain下的目录结构是由一个或多个独立的聚合目录构成,每一个聚合是一个独立的业务功能单元,多个聚合共同实现领域模型的核心业务逻辑。仓储设计时有—个重要原则: 就是一个聚合只能有一个仓储;
  • 基础层有config和util两个子目录;
    5.微服务代码模型_第1张图片

原则

第一点,聚合之间的代码边界一定要清晰。聚合之间的服务调用和数据关联应该尽可能松耦合和低关联,聚合之间的服务调用应该通过上层的应用层组合实现调用,原则上不允许聚合之间直接调用领域服务。这种松耦合的聚合代码关联,在以后业务发展和需求变更时,可以很方便地实现业务功能和聚合代码的重组,在微服务架构演进中将会起到非常重要的作用;
第二点,一定要有代码分层的概念。写代码时一定要搞清楚代码的职责,将它放在职责对应的代码目录内。应用层代码主要完成服务组合和编排,以及聚合之间的协作,它是很薄的一层,不应该有核心领域逻辑代码。领域层是领域模型的业务的核心,领域模型的核心逻辑代码一定要在领域层实现;

2.设计领域模型

领域层的领域对象

领域模型的聚合内一般会有聚合根、实体、值对象、命令和领域事件等领域对象。完成领域故事分析和微服务设计后,微服务的聚合内一般会有聚合根、实体、值对象、领域事件、领域服务、工厂和仓储、持久化对象等领域对象;

A.设计聚台根

  • 聚合根来源于领域模型,需要找出领域模型内与聚合根关联的所有实体和值对象;
  • 聚合根是一种特殊的实体,需要设计它的属性和方法。同时它也可以管理聚合内实体和值对象等领域对象的生命周期。聚合根可以引用聚合内的所有实体,也可以实现聚合之间的基于聚合根ID的引用;
  • 聚合根类放在领域层聚合的entity目录结构下;

B.设计实体

  • 在DDD分层架构里,实体类采用充血模型,在实体类内实现实体的全部业务逻辑。这些实体有自己的业务属性、方法和业务行为;
  • 大多数情况下,领域模型的实体对象与数据库持久化对象是一一对应的。但领域模型的某些实体在微服务没计时,可能会被设计为一个或多个数据持久化实体,或者实体的某些属性会被设计为值对象;
  • 实体类代码对象放在领域层聚合的entity目录结构下;

C.设计值对象

  • 如果这个领域对象在其他聚合内进行生命周期管理,并且引用它的实体对象只允许对它整体替换,我们就可以将它设计为值对象。如果这个领域对象有多条数据记录且需要基于它进行频繁的查询统计,则建议将它设计为实体;
  • 值对象类放在领域层聚合的entity目录结构下;

D.设计领域事件

  • 如果领域模型中领域事件会触发下一步业务操作,就需要设计领域事件;
  • 领域事件实体类放在领域层聚合的event目录结构下。领域事件的订阅建议放在应用层的event目录结构下。领域事件发布相关代码放在领域层或者应用层都是可以的;

E.设计领域服务

  • 领域服务通过对多个实体和实体方法进行组合和编排,完成多个实体组合的核心业务逻辑。领域服务是位于实体方法之上和应用服务之下的一层业务逻辑;
  • 如果实体方法需要被前端应用调用,需要将它封装成领域服务,然后再封装为应用服务;
  • 一个聚合可以建立一个领域服务类,可以将聚合中所有的领域服务都在这个领域服务类中实现。
  • 领域服务类放在领域层聚合的service目录结构下;

F.设计工厂租仓储

  • 一个聚合只有一个仓储。仓储包括仓储接口和仓储实现,通过依赖倒置原则实现应用业务逻辑与数据库资源逻辑的解耦;
  • 工厂类(factory)放在领域层聚合的service目录结构下。仓储相关代码放在领域层聚合的repository目录结构下;

G.设计持久化对象

  • 持久化对象PO主要完成DO对象的数据库持久化操作, PO一般与数据库表是一对一的关系。为了简化数据库设计,减少数据库表的数量,值对象往往以属性嵌入方式或序列化大对象方式嵌入实体表;
  • 持久化对象PO相关代码放在领域层聚合的repository目录结构下;

应用层的领域对象

应用层主要有应用服务和领域事件的发布和订阅;
在严格分层架构模式下,不允许服务的跨层调用,每个服务只能调用它紧邻的下一层服务。服务从下到上依次为:实体方法、领域服务、应用服务和facade接口;

  • 应用服务会对多个领域服务进行组合和编排,在用户接口层完成服务和数据封装后,就可以发布到API网关,供前端应用调用;
  • 应用服务类放在应用层scrvice目录结构下。领域事件的订阅处理逻辑放在应用层event目录结构下;
  • 服务类的命名参考以下规则: 一般一个聚合只有一个应用服务类,服务前面的名称就可以与聚合名保持一致,然后你可以用*DomainService或*AppService作为后缀,来区分它们是领域服务还是应用服务;

3.微服务调用过程

微服务服务封装

  • 在聚合内采用数据强一致性,在聚合之间采用数据最终一致性。在一次事务中,最多只能修攻一个聚合的数据。如果一次业务交易操作涉及了多个聚合数据的修改,那么应采用领域事件驱动机制。微服务内的领域事件通过事件总线完成聚合之间的异步处理,微服务之间的领域事件通过消息中间件完成。一个聚合对应一个聚合代码目录,聚合之间在代码完全隔离,它们通过应用层的应用服务来协调,完成不同聚合领域服务的组合和编排;
  • 实体采用充血模型,在实体类内部实现实体相关的所有业务逻辑,具体实现形式是实体类中的方法。实体是微服务内的原子业务对象,在设计时我们主要考虑实体自身的属性和业务行为,实现领域模型的核心基础能力。实体方法不会过多考虑外部操作和业务流程,保证领域模型的稳定性。聚合根引用实体和值对象,它可以协调聚合内的多个实体,在聚合根类方法中完成多实体的复杂业务逻辑。但为了职责和边界清晰,建议聚合根自身的业务行为在聚合根类方法中实现,而由多个实体组合实现的业务逻辑由聚合内的领域服务完成;
  • DDD提倡富领域模型,尽量将业务逻辑归属到实体对象上,实在无法归属的部分则设计成领域服务。领域服务会对多个实体或实体方法进行组装和编排,实现跨多个实体的复杂核心业务逻辑。领域服务是介于实体和应用服务之间的薄薄的一层,它的主要职能是实现领域层复杂核心领域逻辑的组合和封装。在领域服务或实体方法中,尽量不要调用其他聚合的领域服务或引用其他聚合的实体或值对象;
  • 应用服务用于组合和编排的服务,主要来源于领域服务,也可以来源于外部微服务的应用服务。除了完成服务的组合和编排外,应用服务内还可以完成安全认证、权限校验、初步的数据校验和分布式事务控制等功能;
    5.微服务代码模型_第2张图片

微服务数据形态

5.微服务代码模型_第3张图片

4.微服务解耦策略

微服务之间解耦策略

  • 限界上下文实现了不同业务领域边界的微服务物理边界的解耦;
  • 聚合实现了微服务内不同聚合之间逻辑边界的解耦;
  • 微服务之间通过领域事件和消息中间件,以数据最终—致性的策略,实现了微服务之间的异步调用利服务解耦;
  • 通过适当的数据冗余设计,如值对象的业务快照数据设计,实现了跨微服务不同聚合之间的数据解锅;

微服务内的解耦策略

  • DDD分层架构,通过分层和不同层的职责边界定义,实现了微服务内各层职能和代码的解耦;
  • 用户接口层通过facade接口和数据组装适配,实现了微服务核心业务逻辑与前端应用或用户解耦;
  • 仓储模式通过依赖倒置策略,实现了核心领域逻辑与基础资源处理逻辑的解耦;
  • 微服务代码目录通过聚合目录和分层目录代码边界,实现了不同职能代码边界的解耦,有利于微服务架构演进时代码的组合和拆分;
  • 应用服务通过对不同聚合领域服务的组合和编排,实现了同一个微服务内不同聚合的解耦;
  • 聚合之间通过聚合根ID引用,而不是对象引用方式,完成不同聚合领域对象之间的访问,实现了聚合之间不同领域对象的解耦;
  • 微服务内聚合之间通过事件总线,采用数据最终一致性策略,实现了聚合之间服务同步调用的解耦;

你可能感兴趣的:(#,DDD领域模型,微服务,架构,DDD,java,spring)