- 文章内容全部来自张逸《领域驱动设计实践(战略+战术)》
上文 领域驱动实践
演进后的分层架构代码模型
需求:创建订单,发送邮件通知,异步消息通知仓储系统。
controller是北向网关,承担与用户界面的交互,同理,如果是dubbo,那么dubbo的api也可以视作是北向网关。
南向网关是系统访问外面资源用于支撑业务系统的适配。
- 领域层:包含 PlaceOrderService、Order、Notification、OrderConfirmed 与抽象的 OrderRepository,封装了纯粹的业务逻辑,不掺杂任何与业务无关的技术实现。
- 应用层:包含 OrderAppService 以及抽象的 EventBus 与 NotificationService,提供对外体现业务价值的统一接口,同时还包含了基础设施功能的抽象接口。
- 基础设施层:包含 OrderMapper、RabbitEventBus 与 EmailSender,为业务实现提供对应的技术功能支撑,但真正的基础设施访问则委派给系统边界之外的外部框架或驱动器。
限界上下文与架构
认识限界上下文
限界上下文体现的是一个垂直的架构边界,主要针对后端架构层次的垂直切分。对基础设施,框架的技术选型属于架构设计的考量范围,但不属于限界上下文的代码模型,但这些基础设施,框架的选择仍要考虑他们与限界上下文代码模型的集成、构建与部署。
限界上下文之间应该严格谨守边界,防腐层(ACL)是抵御外部限界上下文变化的最佳场所。
外部资源访问的两种架构风格:
1:数据库共享架构
避免不同限界上下文关联表之间的外键约束。
应该根据领域逻辑知识去识别限界上下文,避免从数据库层面建模。
2:零共享架构
将两个限界上下文共享的外部资源彻底分离。
需要考虑通信的健壮性。
需要保证数据的一致性。
运维和监控的复杂度也会上升。
代码结构
看上面这个图,applicationService是可以不经过domainService直接访问repositories,这就说明了domainService这一层更多的职责应该是领域对象的业务编排,如果一个业务只是简单的持久化一个对象,可以不用经过domainService,这样代码更简洁,避免经过无谓的代码层次,降低可读性。
ordercontext
- gateways
- controllers 北向网关
- OrderController
- messages dto
- CreateOrderRequest
- persistence 南向网关(持久化实现)
- OrderMapper
- client 南向网关(其他微服务/外部服务)
- NotificationClient
- mq mq实现
- RabbitEventBus
- application 业务service
- OrderAppService
- interfaces 基础设施,南向网关的抽象
- client
- NotificationService
- SendNotificationRequest
- mq
- EventBus
- domain 领域服务
- PlaceOrderService
- Order
- OrderConfirmed
- Notification
- NotificationComposer
- repositories 持久化的抽象(持久化也是南向网关的一种,但比较特殊,所以独立出来)
- OrderRepository
不同的上下文之间的通信需要进行隔离
架构风格分析
微服务架构风格
将限界上下文视为微服务。包括用户和外部系统在内的客户端需要通过api Gateway实现微服务的调用。
一个限界上下文未必一定要部署为一个微服务,如果是提供整个系统的公共基础功能,应该定义为公共组件。
微服务架构风格可以保证技术选择,发布节奏的自由,但也会有分布式系统的复杂度,数据一致性和运维部署的挑战。
事件驱动架构风格
将上下文协作模式抽象为发布/订阅事件。系统与外部系统之间需要引入消息中间件,以便于事件消息的传递
CQRS架构风格
在限界上下文层面将查询与命令分为两种不同的实现模式。系统暴露的 API 接口需要分解为命令接口和查询接口,接口类型不同,处理模式和执行方式都不相同。可以为 command 和 query 分别建立 module(领域驱动设计中的设计要素),使得它们的代码模型可以独自演化,毕竟命令和查询的领域模型是完全不同的。甚至可以为同一个领域的 command 和 query 各自建立专有的限界上下文。
BFF
Backends For Frontends,为前端提供的后端。可以看作是业务的聚合,用于承接ui和后端代码。
案例分析
前提:不要以一个码农的视角来看待DDD,不要以一个码农的视角来看项目。
如何主导一个项目的开发
1:清楚项目背景
2:先启阶段:确定系统范围,明确开发目标,预见开发风险,统一技术架构,制定迭代计划。
确定系统范围:包括哪些做,哪些不做;有哪些利益相关方参与。
明确开发目标:包括业务目标,系统性能目标等。
预见开发风险:包括人力资源风险,第三方支持风险,外部政策变化风险等。
统一技术架构:主要指技术架构选型,技术方案制定等。
制定迭代计划:需要有一个明确可执行的迭代计划。
先启阶段最好能有一个mvp计划,明确第一个正式提交版本内容。否则容易迷失在细枝末节的功能需求中。
识别问题域:核心子领域,通用子领域,支撑子领域
统一语言
编写有效用例:分为概要目标,用户目标和子功能
识别参与者,参与者可能是人,也可能是系统、服务或模块。
通过参与者识别用例
使用事件风暴识别限界上下文,避免限界上下文边界过大(2PTs原则)
识别上下文映射(结合用例),避免循坏依赖,如果出现循坏依赖,可能是遗漏了限界上下文,或者上下文之间职责不清
下游调用上游
代码组织架构
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。