目录
引言
最近在重新关注微服务与Service Mesh的实践(后续我们会系统来讲这块),顺便关注了下领域驱动设计(DDD)。本文主要整理对领域驱动设计(DDD)初步了解,并提供了PHP的领域驱动设计(DDD)实践参考代码案例,希望能对大家有所帮助。
一、什么是领域驱动设计(DDD)
领域驱动设计(Domain-Driven Design,简称DDD)是一种以业务领域为核心的软件设计方法论,是由 Eric Evans 在2003年提出的软件开发方法论,通过构建与业务逻辑高度一致的领域模型来解决复杂系统的设计与实现问题。其核心目标是通过领域模型(Domain Model) 的构建,解决复杂业务系统的设计与实现问题。其核心思想是通过深入理解业务本质,将技术实现与业务需求紧密结合,形成可迭代演进的系统架构。DDD 强调以业务领域为核心,通过技术与业务的深度融合,构建可维护、可扩展且易于理解的软件系统。
详细的介绍可以看这里:
《Domain-Driven Design - Tackling Complexity in the Heart of Software》Eric Evans
二、DDD核心思想与价值
聚焦业务领域: 强调将业务规则、流程和概念直接映射到软件模型中,确保软件结构与业务逻辑深度耦合。
统一语言(Ubiquitous Language): 开发团队与业务专家使用一致的术语描述业务逻辑,减少沟通歧义。
分层架构: 通过战略设计划分清晰的业务边界(限界上下文),避免模型污染;通过战术设计实现领域模型的技术落地。
三、DDD关键设计要素
1. 战略设计:划分问题空间
限界上下文(Bounded Context)
限界上下文是 DDD 的核心战略模式,它定义了模型的边界。每个限界上下文对应一个独立的业务子领域,内部使用一致的术语和规则。例如,在电商系统中,各业务领域通过明确的上下文边界实现解耦:
订单上下文(Order Context)
- 核心职责:处理完整的订单生命周期管理
- 功能模块:
订单创建:处理用户下单流程,包括商品选择、优惠计算
支付管理:对接支付网关,处理支付状态变更(待支付/已支付/支付失败)
售后服务:处理退换货申请,包括审核、退款处理
订单状态机:管理从"待付款"到"已完成"的完整状态流转物流上下文(Shipping Context)
- 核心职责:商品配送全流程管理
- 功能模块:
配送规划:基于收货地址自动计算最优配送路线
承运商管理:对接不同快递公司(如顺丰、中通)的API接口
运单管理:生成并跟踪物流单号
配送状态:实时更新"已揽件/运输中/已签收"等状态库存上下文(Inventory Context)
- 核心职责:商品库存的精确管理
- 功能模块:
库存跟踪:实时监控各SKU的库存数量
仓库管理:处理多仓库间的调拨操作
库存预警:设置安全库存阈值,自动触发补货提醒
库存锁定:处理下单时的预扣库存和支付成功时的最终扣减
不同上下文之间通过上下文映射(Context Mapping) 进行交互,例如使用 REST API 或事件驱动机制。
核心域、支撑域与通用域
核心域(Core Domain):决定产品差异化的关键领域(如电商的推荐算法)。
支撑域(Supporting Subdomain):为核心域提供支持(如用户权限管理)。
通用域(Generic Subdomain):可通过现成方案解决的通用问题(如支付网关)。
2. 战术设计:构建领域模型
实体(Entity)
具有唯一标识和生命周期的对象。
值对象(Value Object)
无唯一标识、通过属性定义的对象。
聚合(Aggregate)
一组相关对象的集合,由聚合根(Aggregate Root) 统一管理。例如:
Order 作为聚合根,包含 OrderItem 和 Payment。
外部只能通过聚合根修改内部对象,确保业务一致性。
领域服务(Domain Service)
处理跨聚合或无法归属单一对象的业务逻辑。
领域事件(Domain Event)
表示领域中的重要状态变化。
四、DDD 的实践步骤
1. 事件风暴(Event Storming)
与业务专家协作,通过以下流程梳理需求:
识别领域事件(如“订单已付款”)。
分析触发事件的动作(如“用户点击支付”)。
确定参与的角色与数据(如订单、支付流水)。
2. 模型提炼
使用用例分析和用户故事明确业务场景。
通过四色建模(Fowler)或示例驱动设计(Specification by Example)细化模型。
3. 分层架构
典型的 DDD 分层结构:
用户接口层(Presentation): 处理请求与响应。
应用层(Application): 协调领域对象完成用例。
领域层(Domain): 实现核心业务逻辑。
基础设施层(Infrastructure): 提供技术实现(如数据库、消息队列)。基础设施层应实现领域层定义的接口(依赖反转原则),避免领域层依赖具体技术细节。
//代码示例
// 服务 A(订单服务)
order-service/
├── domain/ # 领域层(定义接口)
├── application/ # 应用层
└── infrastructure/
├── jpa/ # 实现领域层的持久化接口
└── kafka/ # 消息生产实现
// 服务 B(用户服务)
user-service/
├── domain/ # 领域层(定义接口)
├── application/
└── infrastructure/
├── mongodb/ # 实现领域层的持久化接口
└── redis/ # 缓存实现
领域驱动设计中,基础设施层通常是按限界上下文独立实现的,但允许在非领域逻辑(如工具类)上适度共享。核心目标是保持领域层的纯粹性和技术实现的灵活性。
需要避免的陷阱>>
- 过度共享基础设施代码:将技术组件强制统一可能导致服务耦合,违背 DDD 的限界上下文原则。
- 基础设施侵入领域层:领域层不应依赖具体技术框架(如 Spring 注解),而应通过接口抽象技术细节。
微服务架构中的常见实践>>
- 独立基础设施层:在微服务架构中,每个服务通常拥有独立的基础设施层,以实现> > 技术栈的灵活性和服务自治。
- 例外:跨服务的横切关注点(如监控、认证)可通过统一中间件(如 API
Gateway、集中式日志系统)解决,而非代码复用。
4. 代码实现
依赖倒置原则(DIP): 领域层不依赖基础设施。
工厂与仓储(Repository): 封装对象创建与持久化逻辑。
CQRS 模式: 分离查询与命令操作,提升性能。
一般认为只对核心领域作DDD,其他领域服务可以简单化设计。
五、DDD微服务代码实现PHP demo案例
道理讲得天花乱坠,那么我们该如何写起一个微服务Server呢?
可以参考这里:https://github.com/lh16/php-ddd
代码中注释也包含了丰富的层代码实现说明,可以配合本文服用,疗效更好。
六、领域驱动设计DDD与微服务的关系
领域驱动设计(Domain-Driven Design,简称DDD)最初由Eric Evans在其2003年的同名著作中提出,比微服务架构概念的兴起早了近10年。DDD是一套软件开发方法论,专注于复杂业务领域的建模与实现,与具体的技术架构无关。
尽管DDD早于微服务,但微服务架构兴起后,人们发现DDD的多个核心概念与微服务的设计原则高度契合:
- 限界上下文:完美对应微服务的边界划分原则;
- 聚合根:为微服务内部的领域对象管理提供了模式;
- 领域事件:为微服务间的松耦合通信提供了机制。
在现代微服务实践中,DDD已经成为:
- 服务拆分的重要依据(基于业务能力而非技术划分);
- 服务内部实现的标准方法(领域模型驱动开发);
- 跨服务协作的指导原则(通过上下文映射处理服务间关系);
这种协同效应使得DDD在微服务时代获得了比早期更广泛的关注和应用。
参考资料:
https://en.wikipedia.org/wiki/Domain-driven_design
https://baijiahao.baidu.com/s?id=1822762023490905665&wfr=spider&for=pc
https://github.com/wmaozhi/Domain-Driven-Design-zh/blob/master/docs/README.md
https://www.jianshu.com/p/4a0d89dd7c20