DDD战略5 代码模型
2019-05-02 本文已影响0人
莫小归
GitChat课程《领域驱动设计--战略篇》笔记,课程作者张逸
一.遵循DDD思想的代码模型
- 考虑
1)层与模块之间的职责分离与松散耦合
2)将整个限界上下文作为基本设计单元,并照顾到限界上下文之间的协作关系
1.代码模型1
- application
- interfaces
- domain
- repositories
- gateways
- controllers
- persistence
- mq
- client
- ...
- application 对应DDD的应用层,主要内容为该限界上下文中所有的应用服务
- interfaces 对gateways中除persistence之外的抽象,包括访问除数据库之外其他外部资源的抽象接口,以及访问第三方服务或其他限界上下文服务的抽象接口
- domain 对应DDD的领域层,为了更好地体现repositories在基础设施层扮演的与外部资源打交道的网关语义,将repositories单独分离
- repositories 代表DDD中战术设计阶段的资源库,如果不复杂,可合并到domain
- gateways 对应DDD的基础设施层,其下可以视外部资源的集成需求划分不同的包
1)controller是对客户端提供接口的北向网关,等同于上下文映射中开放主机服务(OHS)的概念
2)persistence对应了repositories抽象
3)其余网关对应interfaces抽象,其中client包下的实现类与interfaces下的对应接口组合起来,等于上下文映射中“防腐层(ACL)”的概念
2.代码模型2
- application
- domain
- interfaces
- repositories
- mq
- acl
- gateways
- ohs
- persistence
- mq
- acl
- ...
二.进程间通信的代码模型
- 进程间通信意味着每个限界上下文就是一个独立的部署单元,此即微服务的意义
- 由于微服务的粒度较小,其代码模型一般采用命名空间级别的方式,整个微服务的代码模型生成一个JAR包即可
1.以下订单案例中订单上下文与通知上下文为例
- 原代码架构如下
- 进程间通信新代码结构如下
- 进程间通信代码架构图
三.进程内通信的代码模型
- 需要注意在代码模型中体现限界上下文的边界,妥善考虑两个处于相同进程中的限界上下文彼此之间如何协作
1.对各种因素的考量
1)简单:在下游限界上下文的领域层直接实例化上游限界上下文的领域类
2)解耦:在下游限界上下文的领域层通过上游限界上下文的接口和依赖注入进行调用
3)迁移:在下游限界上下文中定义一个防腐层,而非直接调用
4)明晰:为保证领域层代码的纯粹性,避免在当前限界上下文中依赖不属于自己的代码模型
2.仍以下订单场景为例
- 代码架构图
区别在于NotificationClient不再通过跨进程调用的方式发起对RESTful服务的调用,即使在Notification Context中定义了这样的OHS。而是直接通过实例化的方式调用Notification Context应用层的Notification AppService。这是在OrderContext中唯一与NotificationContext产生依赖的地方
3.总结
- 可见,即使限界上下文采取进程内通信,也仅仅是封装在防腐层中发起调用的实现有所不同(即上例的NotificationClient),这并不影响设计的代码模型。之所以这样设计,理由有二
1)通信边界的划分是物理意义,代码模型的划分是逻辑意义,二者互相并不影响
2)为保证系统从单体架构向微服务架构迁移,应保证代码结构不受架构风格变化的影响
四.代码模型的架构决策
1.代码模型的定义
- 代码模型属于软件架构的一部分,是设计模型的进化与实现,体现了代码模型(包)的结构层次。在架构视图中,代码模型甚至会作为其中的一个视图,通过它来展现模块的划分,并定义运行时实体与执行视图的联系
- 在DDD背景下,代码模型的设计可分为两个层次
1)系统层次:设计整个软件系统的代码模型
2)限界上下文层次:设计限界上下文内部的代码模型,该层次的代码设计之前已经涉及
2.系统层次的代码模型设计
- 针对整个软件系统的代码实际将限界上下文视为黑盒子,仅关心限界上下文暴露的接口以及它们之间的协作关系,同时对于整个软件系统,保留其在架构风格上的一致性
- 系统层次的风格为组件(限界上下文)的协作提供了抽象,不同的架构风格适用于不同的应用场景
1)如果将限界上下文视为微服务,则该系统是微服务架构风格
2)如果将上下文协作模式抽象为发布/订阅事件,则该系统是事件驱动架构风格
3)如果在限界上下文层面将查询与命令分为两种不同的实现模式,则该系统是命令查询职责分离(CQRS)架构风格 - 结合C4模型考虑元素的粒度和层次
1)系统上下文:最高的抽象层次,代表了能够提供价值的东西,一个系统由多个独立的容器组成
2)容器:一个在其内部可以执行组件或驻留数据的东西。作为系统的一部分,容器通常是可执行文件,但未必是给独立的线程,任何容器间的通信可能都需要一个远程接口
3)组件:一个或多个类组成的逻辑群组,组件通常由多个类在更高层次的约束下组合而成
4)类:在一个面向对象的世界里,类是软件系统的最小结构单元
- 在系统上下文层次,我们需要将系统作为一个完整单元,然后思考它和用户之间的交互方式,需要集成的外部系统有哪些,采用怎样的通信协议。例如
1)微服务的架构风格,意味包括用户和外部系统在内的客户端都需要通过API Gateway实现微服务的调用
2)事件驱动的架构风格,意味系统与外部系统之间需要引入消息中间件便于事件消息的传递
3)CQRS的架构风格,意味系统暴露的API接口需要分解为命令接口和查询接口,接口类型不同,处理模式和执行方式都不相同 - C4模型中的容器基本等同于微服务(限界上下文)的概念,容器与组件之间的边界很模糊,这取决于限界上下文之间通信机制的决策
- 为整个系统提供公共基础功能的限界上下文不一定要部署为微服务,它可能以公共组件的形式而存在。这种公共组件又分为两种
1)具有业务价值的公共组件,对应于一个限界上下文,将其设计为公共组件是因为需求稳定、与其协作时需要传输大量数据、无需访问外部资源
2)提供公共基础设施功能(如文件读写、FTP传输、Telnet通信等)的公共组件,