DDD领域驱动设计实战-分层架构及代码目录结构

2.2 应用层


特点

  • 关心处理完一个完整的业务

  • 该层只负责业务编排,对象转换,实际业务逻辑由领域层完成

  • 不关心【请求从何处来】,但是关心【谁来、做什么、有没有权限做】

即复制安全认证、权限校验

  • 集成不同的领域服务解决问题

应用层位于领域层之上,因为领域层包含多个聚合,所以它可协调多个聚合服务和领域对象完成服务编排和组合,协作完成业务。

  • 最终一致性(最终一致性对业务有侵入)事务放到这层

  • 对应到分布式系统中的中台等概念

  • 方法级别的功能权限控制放到这层

  • 只产应用异常,对应 HTTP 状态码 403、401

  • 准单体系统下,按照应用划分模块

主要包含应用服务,理论上不应有业务规则或逻辑,而主要是面向用例和流程相关的操作。

  • 应用层也是微服务间的交互通道,它可调用其它微服务,完成微服务间的服务组合和编排

开发设计时,不要将本该放在领域层的业务逻辑放到应用层。庞大的应用层会使领域模型失焦,时间一长,微服务就会退化为MVC

应用服务是在应用层,负责

  • 服务的组合、编排、转发、转换和传递,处理业务用例的执行顺序以及结果的拼装,以粗粒度服务通过API网关发布到前端

  • 发送或订阅领域事件

应用层代码目录结构

存放应用层服务组合和编排相关的代码。

应用服务向下基于

  • 微服务内的领域服务,或

  • 外部微服务的应用服务

完成服务的编排和组合

向上为用户接口层提供各种应用数据展现支持服务。

应用服务和事件等代码会放在这层目录。

Event(事件)

主要存放事件相关代码。包括两子目录:

publish

主要存放事件发布相关代码。比如发布用户创建事件给其它微服务。

subscribe

主要存放事件订阅相关代码(事件处理相关的核心业务逻辑在领域层实现)。

虽然应用层和领域层都可进行事件的发布和处理,但为实现事件的统一管理,推荐将微服务内所有事件的发布和订阅处理都统一放到应用层,事件相关的核心业务逻辑实现放在领域层。通过应用层调用领域层服务,来实现完整的事件发布和订阅处理流程。

Service(应用服务)

应用服务会对多个领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度的服务。应用服务主要实现服务组合和编排,是一段独立的业务逻辑。

可以将所有应用服务放在一个应用服务类里,也可以把一个应用服务设计为一个应用服务类,以防应用服务类代码量过大。

比如内部服务->创建用户;外部服务->创建日志。

2.3 领域层


主要包含聚合根、实体、值对象、领域服务等领域模型中的领域对象。

实现核心业务逻辑,通过各种校验保证业务正确性。领域层主要体现领域模型的业务能力,它用来表达业务概念、业务状态和业务规则。

领域模型的业务逻辑主要由实体和领域服务实现:

  • 实体采用充血模型 实现所有与之相关的业务功能。

实体和领域服务在实现业务逻辑上不是同级,当领域中的某些功能,单一实体或值对象无法实现,就会用到领域服务,它可组合聚合内的多个实体或值对象,实现复杂业务逻辑。

3 Domain(领域层)

============================================================================

存放领域层核心业务逻辑相关的代码。

可包含多个聚合代码包,共同实现领域模型的核心业务逻辑。聚合以聚合内的实体、方法、领域服务和事件等代码会放在该层目录。

领域层包括一个或多个聚合的实体类、事件实体类、领域服务以及工厂、仓储相关代码。一个聚合对应一个聚合代码目录,聚合之间在代码上完全隔离,聚合之间通过应用层协调。

Domain 由一或多个聚合包构成,共同实现领域模型的核心业务逻辑。

聚合内的代码模型是标准和统一的,包括:entity、event、repository、service 子目录

Aggregate(聚合)


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

以聚合为单位的代码放在一个包里的主要是为业务内聚,更是为以后微服务之间聚合的重组。聚合之间清晰的代码边界,可让你轻松地实现以聚合为单位的微服务重组。

实例

比如进入用户聚合目录下(如CustomerAggregate)。

假设这样一个场景,主播账户作为一个聚合,优惠券模块作为一个聚合。那主播选券的命令属于主播账户聚合。然后主播账户里的优惠券就是这个聚合里的值对象。

如果有多个聚合, 比如聚合根A和聚合根B, 从业务的角度讲,可以接受AB间数据的最终一致性,但从数据展示的角度考虑, A和B是有强关联性的,也就是说在页面上,他们总是一起在页面的某部分出现, 那可以分别调两个聚合的领域服务,然后将两个聚合根的DO对象转换为一个DTO,就可以给前端提供包含两个聚合数据的数据服务了。

细分结构
Entity(实体)

存放聚合根、实体、值对象以及工厂模式(Factory,工厂模式主要是实现复杂聚合的实体的数据初始化。如果实体太多,聚合根处理起来会很复杂,通过工厂一次初始化)相关代码。实体类采用充血模型,同一实体相关的业务逻辑都在实体类代码中实现。跨实体的业务逻辑代码在领域服务中实现。比如用户聚合根。

Event(事件)

存放事件实体以及与事件活动相关的业务逻辑代码。比如创建用户的事件。

Service(领域服务)

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

Repository(仓储)

存放所在聚合的查询或持久化领域对象的代码,通常包括仓储接口和仓储实现方法。为了方便聚合的拆分和组合,设定原则:一个聚合对应一个仓储。比如将用户信息保存到数据库。

按DDD分层架构,仓储实现本应属基础层代码,但为在微服务架构演进时,保证代码拆分和重组的便利性,把聚合仓储实现的代码放到聚合包内。这样,如果需求或设计发生变化导致聚合需要拆分或重组,就可将包括核心业务逻辑和仓储代码的聚合包整体迁移,轻松实现微服务架构演进。

2.4 基础层


为其它各层提供通用技术基础服务:

  • 三方工具

  • 驱动

  • MQ

  • API网关

  • 文件

  • 缓存

  • DB

最常用的

基础层包含基础服务,它采用依赖反转,封装基础资源服务,实现应用层、领域层与基础层解耦。

MVC架构由于上层应用对DB强耦合,很多公司在架构演进最怕换DB,一旦更换,可能需重写一堆代码。

但采用依赖反转,应用层即可通过解耦保持独立核心业务逻辑。当DB变更,只需更换DB基础服务。

4 Infrastructure(基础层)

====================================================================================

主要存放基础资源服务相关的代码,为其它各层提供的通用技术能力、三方软件包、数据库服务、配置和基础资源服务的代码都会放在这一层目录里。

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

  • Config

主要存放配置相关代码。

  • Util

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

3 微服务架构演进

========================================================================

领域模型中对象的层次从内到外依次是:值对象、实体、聚合和限界上下文。

实体或值对象的简单变更,一般不会让领域模型和微服务发生大变。但聚合的重组或拆分却可以。因为聚合内业务功能内聚,能独立完成特定业务。那聚合的重组或拆分,势必引起业务模块和系统功能变化。

可以聚合为基础单元,完成领域模型和微服务架构的演进。

聚合可作为整体,在不同领域模型间重组或拆分,或直接将一个聚合独立为微服务。

微服务架构的演进案例


现有

微服务 1:包含聚合 a、b、c

微服务2:

微服务3:包含聚合 d、e、f

  • 当发现微服务1中聚合a的功能经常被高频访问,以致拖累了整个微服务1的性能,可把聚合a,从微服务1中剥离,独立为微服务2以应对高性能场景

  • 随业务发展,发现微服务3的领域模型变化,聚合d会更适合放到微服务1的领域模型。即可将聚合d整体迁移到微服务1。注意定义好聚合间的代码边界

  • 架构演进后,微服务1从最初包含聚合a、b、c,演进为包含聚合b、c、d的新领域模型和微服务

可见,好的聚合和代码模型的边界设计,可让你快速应对业务变化,轻松实现领域模型和微服务架构演进。

微服务内服务的演进

========================================================================

在微服务内部,实体的方法被领域服务组合和封装,领域服务又被应用服务组合和封装。在服务逐层组合和封装的过程中,你会发现这样一个有趣的现象。

服务设计时,你并不一定能完整预测有哪些下层服务会被多少个上层服务组装,因此领域层通常只提供一些原子服务,比如领域服务a、b、c。但随系统功能增强和外部接入越来越多,应用服务不断丰富。终有一日,你会发现领域服务b和c同时多次被多个应用服务调用了,执行顺序也基本一致。这时你可以考虑将b和c合并,再将应用服务中b、c的功能下沉到领域层,演进为新的领域服务(b+c)。这样既减少了服务的数量,也减轻了上层服务组合和编排的复杂度。

这就是服务演进,领域模型会越来越能适应需求快速变化。

从MVC跨越到DDD

=========================================================================

由于层间松耦合,可专注本层设计,而不必关心其它层,也不必担心自己的设计会影响其它层。即DDD成功降低层与层之间的依赖。

分层架构使得程序结构更清晰,升级和维护更容易。修改某层代码时,只要本层接口参数不变,其它层不必修改。即使本层接口发生变化,也只影响相邻的上层,修改工作量小且可控。

传统企业应用大多是单体架构,而单体架构则大多是三层架构。三层架构解决了程序内代码间调用复杂、代码职责不清的问题,但这种分层是逻辑概念,在物理上它是中心化的集中式架构,并不适合分布式微服务架构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值