DDD-4层架构
优点:通过聚合、限界上下文等模式,可以有效的支持和管理复杂业务逻辑。代码的可读性、可维护性更高。比如基础组件的依赖一般都会增加防腐层,使得组件的替换是可插拔的,不会影响到内部的业务逻辑。
缺点:领域模型、限界上下文的拆分复杂,很少有系统一上来就应用DDD架构,一般都是对旧有架构的重构,而这中间的切换和重构成本太高。
业务逻辑:业务逻辑主要封装在领域模型中
领域模型:以领域行为和规则驱动设计(充血模型)。
DDD分为哪4层
-
用户展示层(User Interface): 客户端统一入口,它只用于处理用户展示和用户请求,不会包含业务逻辑。对外的接口定义也可以放在这一层。主要功能如下:
- 用户界面层也有它自己的逻辑,例如对用户的输入参数进行验证,但这种验证要跟领域层的验证逻辑区分开。比如空值校验、数据类型校验。
- 用户界面层的请求和响应参数不在此层中定义,而是定义到了应用服务层,这样做的好处就是少了一次参数模型转换。
- 用户界面层的响应实体要在此层中定义并转换,因为为了兼容多个用户界面层,应用服务层只会返回跟业务相关的领域模型。
-
应用服务层(Application):对应传统的Service,只做业务编排,不做复杂业务逻辑。
-
业务领域层(domain): 用于业务逻辑的聚合,并且使用依赖倒置。
- 它不依赖其他任何的外部组件,领域层的职责就是通过领域实体和领域服务来聚合业务逻辑;领域服务并不是必须的,如果业务逻辑都可以用领域实体来完成,这个时候就不需要领域服务;那领域服务主要是做那些事情的呢?
- 领域服务的职责:不属于单个聚合根的业务,需要用领域服务去协调多个聚合完成业务逻辑。
- 调用其他外部服务(如RPC)处理业务的,在拿到外部模型后,可能需要在领域服务中做业务校验或领域模型转换
- 领域层中定义基础设施接口,例如资源库repository、远程服务调用remote、消息事件mq,然后由基础设施层实现。
- 一个service的方法操作xx个(建议2个)以上不同的聚合(或实体)或存在外部上下文依赖时,必须使用domain service
- 抽象到domain service的方法,原则上每个方法代表一个聚合的一系列逻辑处理和一个聚合(或实体)的数据库写操作;
-
基础设施层(Infrastructure):
- dao层:
- Remote 远程服务层:外部服务、其他服务的限界上下文的远程调用
- Mq: 消息组件
- Share 共享层:公共组件、基础常量、工具类、缓存、拦截器等
如何理解限界上下文?
每个上下文可以简单对应一个业务系统,但并不严格一一对应,比如订单上下文、商品上下文、调度上下文等。订单上下文中汇聚和商品信息、地址信息、用户信息等。
如何实践?
传统的设计,一般是自下而上,也就是先设计好数据模型,然后再把业务逻辑统一封装到Service中,最后再controller层封装响应结果。DDD的设计需要自上而下,先设计领域模型,再去设计数据模型。设计的关注点在于业务,而不是数据。
聚合是由一系列相关的领域模型组合而成,例如订单域:Order(订单) + OrderItem(订单项) + DeliveryInfo(配送地址)可以组成一个聚合,从订单上下文的角度来说这个聚合逻辑的聚合根就是Order。
领域模型: 数据模型 + 领域服务,这里的数据模型可以是充血模型。
业务编排的概念,简而言之就是:业务逻辑无交叉,一行代码仅代表一个业务行为、用例或用户故事。
举例:DDD的实践
- 邀请多方参与人一起讨论,特别是产品,研发、测试和架构师。
- 进行领域建模,分为两个步骤, 第一个是发散阶段, 产生很多实体、命令、事件等领域对象;第二步是收敛,我们从不同的维度对进行聚类形成聚合,建立最终领域模型
- 发散阶段:领域对象分析,也就 实体、值对象、领域事件、领域命令的分析。
- 收敛阶段:构建聚合,那么首先就要在实体中找到聚合根。
一个 聚合根的特点:
- 聚合根一定是实体,那么它具有全局唯一的标识
- 聚合根是具备生命周期的
- 聚合根需要专门的模块来进行管理
- 划分边界上下文 领域的拆分:按业务抽象拆分、一个业务可以划分好几个子域 防腐层:
- 一个域在访问其他域的模型时,把获取到的模型做层转换映射到自己域的模型中(不直接使用别的域模型作为自己域模型中的一部分);
- 防止源域模型发生变更,依赖源域模型的调用方,在需要源域模型新功能时,必须要全局依赖修改,才在能兼容
- 领域服务设计,将模型映射到工程代码中