设计值对象
根据需要将某些实体的某些属性设计为值对象。值对象类放在代码模型的Entity目录。在个人用户聚合中,用户拥有用户证件类型,它以枚举值形式存在,所以将其设计为值对象。
如果这个领域对象在其它聚合内维护生命周期,且在它依附的实体对象中只允许整体替换,即可设计为值对象。
如果这个对象是多条且需基于其做查询统计,推荐设计为实体。
设计领域事件
如果领域模型中领域事件会触发下一步的业务操作,我们就需要设计领域事件。首先确定领域事件发生在微服务内还是微服务之间。然后设计事件实体对象,事件的发布和订阅机制,以及事件的处理机制。判断是否需要引入事件总线或MQ。
在个人用户聚合中有用户已创建的领域事件,因此它有用户创建事件这个实体。
领域事件实体和处理类放在领域层的Event目录。领域事件的发布和订阅类推荐放在应用层的Event目录。
设计领域服务
如果一个业务动作或行为跨多个实体,我们就需要设计领域服务。领域服务通过对多个实体和实体方法进行组合,完成核心业务逻辑。
领域服务是位于实体方法之上和应用服务之下的一层业务逻辑。
按严格分层架构,如果实体的方法需要暴露给应用层,它需要封装成领域服务后才可以被应用服务调用。所以如果有的实体方法需要被前端应用调用,我们会将它封装成领域服务,然后再封装为应用服务。
个人客户聚合根这个实体创建个人客户信息的方法,被封装为创建个人客户信息领域服务。然后再被封装为创建个人客户信息应用服务,向前端应用暴露。
领域服务类放在领域层的Service目录。
设计仓储
每一个聚合都有一个仓储,仓储主要用来完成数据查询和持久化操作。仓储包括仓储的接口和仓储实现,通过依赖倒置实现应用业务逻辑与数据库资源逻辑的解耦。
仓储代码放在领域层的Repository目录结构下。
应用层的领域对象
应用层的主要领域对象是应用服务和事件的发布以及订阅。
在事件风暴或领域故事分析时,我们往往根据用户或系统发起的命令,设计服务或实体方法。为了响应这个命令,我们需要分析和记录:
- 在应用层和领域层分别会发生哪些业务行为
- 各层分别需要设计哪些服务或者方法
- 这些方法和服务的分层以及领域类型(比如实体方法、领域服务和应用服务等),它们之间的调用和组合的依赖关系
严格分层架构不允许服务的跨层调用,每个服务只能调用它的下一层服务。服务从下到上依次为:实体方法、领域服务和应用服务。
如需实现服务的跨层调用,推荐采用服务逐层封装。
服务的封装和调用主要有以下几种方式。
- 实体方法的封装
实体方法是最底层的原子业务逻辑。如果单一实体的方法需要被跨层调用,可将它封装成领域服务,这样封装的领域服务就可被应用服务调用和编排。
如果它还需要被用户接口层调用,还需将这个领域服务封装成应用服务。经过逐层服务封装,实体方法即可暴露给上面不同层,实现跨层调用。
封装时服务前面的名字可以保持一致,可用*DomainService或*AppService后缀来区分领域服务或应用服务。
- 领域服务的组合和封装
领域服务会对多个实体和实体方法进行组合和编排,供应用服务调用。如果它需要暴露给用户接口层,领域服务就需要封装成应用服务。 - 应用服务的组合和编排
应用服务会对多个领域服务进行组合和编排,暴露给用户接口层,供前端应用调用。
多个应用服务可能会对多个同样的领域服务重复进行同样业务逻辑的组合和编排。当出现这种情况时,你就需要分析是不是领域服务可以整合了。你可以将这几个不断重复组合的领域服务,合并到一个领域服务中实现。这样既省去了应用服务的反复编排,也实现了服务的演进。这样领域模型将会越来越精炼,更能适应业务的要求。
应用服务类放在应用层Service目录。领域事件的发布和订阅类放在应用层Event目录。

