DDD领域设计【一、简介】
领域驱动设计(Domain-Driven Design, DDD) 是由 Eric Evans 于 2003 年提出的一套应对复杂业务系统的软件开发方法论。其核心思想是 将业务领域作为软件设计的核心,通过建立精准的领域模型来分离业务复杂性与技术复杂性,最终实现代码与业务的高度一致。
DDD 主要分为两大阶段:战略设计(解决系统如何拆分、边界在哪)和 战术设计(解决代码如何组织、模型如何构建)。以下是详细的名词与思想解析。
一、DDD 核心思想
1. 核心哲学
- 业务优先:技术为业务服务,深度理解业务是架构设计的前提。
- 统一语言:开发、产品、业务专家使用同一套术语,消除沟通壁垒。
- 模型驱动:通过领域模型表达业务规则,模型即业务,代码即模型。
- 分而治之:将庞大系统拆分为高内聚、低耦合的小模块(限界上下文)。
2. 两大阶段
- 战略设计(Strategic Design):做什么。划分业务边界、确定系统架构、明确团队分工。
- 战术设计(Tactical Design):怎么做。在边界内构建领域模型、设计代码结构、实现业务逻辑。
二、战略设计核心名词(宏观架构)
1. 领域 (Domain)
- 定义:软件要处理的业务问题范围和相关知识集合。
- 理解:如电商领域、医疗领域、金融领域。它是所有设计的出发点。
2. 子域 (Subdomain)
- 定义:领域的细分模块,一个领域由多个子域构成。
- 分类(核心):
- 核心域 (Core Domain):业务的核心竞争力,需重点投入、自主研发。(例:电商的交易系统)
- 支撑域 (Supporting Domain):支撑核心业务,但非差异化优势。(例:电商的物流管理)
- 通用域 (Generic Domain):通用功能,无业务特性,可外包或使用开源。(例:用户认证、日志)
3. 限界上下文 (Bounded Context, BC)
- 定义:DDD 最重要的概念。模型语义生效的边界。
- 作用:
- 每个 BC 内有独立的通用语言和独立的领域模型。
- 解决“一词多义”问题。例如:
产品在商品上下文是描述、价格;在物流上下文是重量、体积。
- 关系:通常一个限界上下文对应一个微服务或一个开发团队。
4. 上下文映射 (Context Mapping)
- 定义:描述限界上下文之间的协作关系与集成方式。
- 常见模式:
- 客户-供应商 (Customer-Supplier):下游依赖上游,上游主导。
- 防腐层 (Anti-Corruption Layer, ACL):下游通过适配层隔离上游,保护自身模型纯净。
- 共享内核 (Shared Kernel):两个团队共享一小部分模型(慎用,易耦合)。
- 发布-订阅 (Publish-Subscribe):通过领域事件解耦通信。
5. 统一语言 (Ubiquitous Language)
- 定义:团队共同创建的业务术语词典。
- 要求:术语必须一致地出现在代码、文档、日常对话中。
- 价值:消除“业务说业务,开发做开发”的鸿沟。
三、战术设计核心名词(微观建模)
1. 实体 (Entity)
- 定义:拥有唯一标识符(ID),属性可变但身份不变的对象。
- 特征:有生命周期、有状态、需被追踪。
- 示例:
用户(User)、订单(Order)。ID 不变,订单状态可从待付款变为已完成。
2. 值对象 (Value Object)
- 定义:没有唯一ID,由属性值定义的对象。
- 特征:不可变、无生命周期、可替换。
- 示例:
地址(Address)、金额(Money)、颜色(Color)。 - 原则:描述实体的特征,但本身不具备独立存在意义。
3. 聚合 (Aggregate) & 聚合根 (Aggregate Root)
- 定义:一组高度关联的实体与值对象的集合,是数据修改与持久化的基本单元。
- 聚合根 (AR):聚合的入口/管理者,是一个特殊的实体。
- 规则(核心):
- 外部只能通过聚合根访问聚合内部对象。
- 事务边界:一个事务只能修改一个聚合。
- 一致性:聚合根负责维护内部数据的业务一致性。
- 示例:
订单(Order)是聚合根,包含订单项(OrderItem)、收货地址(Address)。外部只能调用Order的方法,不能直接修改OrderItem。
4. 领域服务 (Domain Service)
- 定义:封装不适合放在单个实体/值对象上的无状态业务逻辑。
- 场景:跨多个实体、跨聚合的业务逻辑。
- 示例:
支付服务(PaymentService)、价格计算服务(PricingService)。 - 原则:只做纯业务逻辑,不处理IO、事务、权限。
5. 领域事件 (Domain Event)
- 定义:领域中发生的有业务意义的状态变更。
- 作用:解耦。一个聚合发布事件,其他聚合/上下文异步订阅处理。
- 示例:
订单已支付(OrderPaidEvent)→ 触发库存扣减、物流发货。 - 优势:最终一致性,高扩展性。
6. 仓储 (Repository)
- 定义:聚合根的容器,提供持久化(增删改查)能力。
- 作用:隔离领域层与数据库。领域层只依赖 Repository 接口,具体实现(MySQL/Mongo)在基础设施层。
- 原则:一个聚合根对应一个 Repository。
7. 工厂 (Factory)
- 定义:封装复杂对象(聚合)的创建逻辑。
- 作用:隐藏创建细节,保证对象创建时即完整有效。
四、DDD 标准分层架构
DDD 通过分层隔离关注点,领域层绝对独立,不依赖任何框架或技术细节。
1. 用户接口层 (Interfaces)
- 职责:处理外部请求(HTTP/RPC)、参数校验、返回DTO。
- 组件:Controller、Facade、DTO 转换器。
2. 应用层 (Application)
- 职责:非常薄。只编排,不实现业务。
- 工作:调用领域服务/聚合根、事务控制、权限校验、发送领域事件。
- 组件:XXXApplicationService。
3. 领域层 (Domain) —— 核心层
- 职责:表达所有业务概念、规则、状态。
- 组件:Entity、VO、Aggregate、Domain Service、Domain Event、Repository 接口。
- 原则:纯业务,无任何技术代码(无Spring、无JDBC)。
4. 基础设施层 (Infrastructure)
- 职责:提供技术支撑。
- 组件:Repository 实现(MyBatis/JPA)、消息队列、日志、缓存、第三方SDK。
五、总结:DDD 核心关系图
领域 (Domain)
↳ 子域 (Subdomain) [核心/支撑/通用]
↳ 限界上下文 (Bounded Context) [微服务]
↳ 统一语言 (Ubiquitous Language)
↳ 领域模型 (Domain Model)
↳ 聚合 (Aggregate)
↳ 聚合根 (Aggregate Root) [实体]
↳ 实体 (Entity)
↳ 值对象 (Value Object)
↳ 领域服务 (Domain Service)
↳ 领域事件 (Domain Event)
↳ 仓储接口 (Repository)
↳ 应用服务 (Application Service) [编排]
↳ 基础设施 (Infrastructure) [技术实现]
一句话记住 DDD:战略划边界,战术建模型;领域为核心,统一语言贯穿始终。
下面是「电商订单业务」完整的 DDD 实战流程。
事件风暴 → 领域划分 → 限界上下文 → 聚合设计 → 代码结构 → 完整业务流程
一、业务场景(先明确我们要做什么)
简化版电商流程:
- 用户创建订单(选择商品、收货地址、支付方式)
- 系统校验库存、价格、优惠券
- 订单提交成功 → 待支付
- 用户支付 → 订单变成已支付
- 触发发货、扣减库存、通知物流
我们就用这个流程做一次完整 DDD 建模。
二、第一步:事件风暴(DDD 最核心的拆分方法)
事件风暴的目的只有一个:
把业务拆干净,找到领域、子域、限界上下文
1. 找出所有领域事件(Domain Events)
领域事件 = 业务上发生了有意义的事
我们先全部列出来:
- OrderCreatedEvent(订单已创建)
- OrderSubmittedEvent(订单已提交)
- OrderPaidEvent(订单已支付)
- OrderCancelledEvent(订单已取消)
- StockDeductedEvent(库存已扣减)
- ShipmentCreatedEvent(物流单已创建)
2. 找出触发事件的命令(Command)
- CreateOrderCommand
- SubmitOrderCommand
- PayOrderCommand
- CancelOrderCommand
3. 找出参与的实体(Entity)
- User(用户)
- Order(订单)
- OrderItem(订单项)
- Product(商品)
- Inventory(库存)
- Shipment(物流单)
- Payment(支付单)
4. 聚合分组(关键一步)
根据业务内聚性分组:
聚合 1:订单聚合
- Order(聚合根)
- OrderItem
- Address(值对象)
- OrderAmount(值对象)
聚合 2:商品库存聚合
- Inventory(聚合根)
- Product
聚合 3:支付聚合
- Payment(聚合根)
聚合 4:物流聚合
- Shipment(聚合根)
三、第二步:战略设计——划分领域 & 限界上下文
1. 领域 Domain
电商业务领域
2. 子域 Subdomain
- 核心域:交易订单域(竞争力核心)
- 支撑域:商品域、库存域、物流域
- 通用域:用户域、支付域(可接第三方)
3. 限界上下文 Bounded Context
最终拆分出 4 个 BC(对应微服务):
- 用户上下文(User BC)
- 订单上下文(Order BC) —— 核心
- 商品库存上下文(Product BC)
- 支付上下文(Payment BC)
- 物流上下文(Shipment BC)
一个限界上下文 ≈ 一个微服务
内部有自己独立的模型、语言、数据库
四、第三步:战术设计——订单聚合模型(最核心)
1. 订单聚合结构
Order(聚合根 · 实体)
├─ OrderId(ID)
├─ UserId
├─ Address(值对象)
├─ OrderStatus(枚举)
├─ totalAmount(值对象)
├─ List<OrderItem>(子实体)
└─ 行为方法:submit()、pay()、cancel()…
OrderItem(子实体)
├─ productId
├─ price
└─ count
Address(值对象)
OrderAmount(值对象)
2. 实体 vs 值对象 清晰区分
- 实体:有 ID、会变、有生命周期
Order、OrderItem、Payment - 值对象:无 ID、不可变、只描述
Address、Money、Coordinate、OrderAmount
3. 聚合根的规则(非常重要)
- 外部只能访问聚合根,不能直接操作内部子实体
- 一个事务只修改一个聚合
- 聚合根保证业务不变性规则
例:订单已支付不能再修改数量
五、第四步:DDD 四层架构 + 代码结构(可直接复制用)
标准 DDD 包结构(Java 风格,通用所有语言)
order/
├─ interfaces/ # 接口层:Controller、DTO
├─ application/ # 应用层:编排业务
├─ domain/ # 领域层(核心)
│ ├─ entity/ # 实体、聚合根
│ ├─ vo/ # 值对象
│ ├─ event/ # 领域事件
│ ├─ service/ # 领域服务
│ └─ repository/ # Repository 接口
└─ infrastructure/ # 基础设施层
└─ repository/impl # Repository 实现(MyBatis/JPA)
六、第五步:代码实战(最关键的业务逻辑)
我直接写订单创建 → 提交 → 支付的真实 DDD 风格逻辑。
1. 领域层:订单聚合根(核心业务规则在这里)
// 聚合根
public class Order {
private OrderId id;
private UserId userId;
private Address address;
private OrderStatus status;
private OrderAmount totalAmount;
private List<OrderItem> items;
// 订单提交
public void submit() {
if (this.status != OrderStatus.DRAFT) {
throw new IllegalStateException("只能提交草稿订单");
}
this.status = OrderStatus.PENDING_PAY;
// 发布领域事件
DomainEventPublisher.publish(new OrderSubmittedEvent(id));
}
// 订单支付
public void pay() {
if (this.status != OrderStatus.PENDING_PAY) {
throw new IllegalStateException("订单状态不正确");
}
this.status = OrderStatus.PAID;
DomainEventPublisher.publish(new OrderPaidEvent(id, userId));
}
}
重点:
所有业务规则、校验、状态变更都在领域类内部
不在 Service 里乱写
2. 领域事件(跨服务解耦神器)
public class OrderPaidEvent {
private OrderId orderId;
private UserId userId;
}
订单支付后,发布事件:
- 库存服务监听 → 扣库存
- 物流服务监听 → 创建物流单
- 消息服务监听 → 发推送
完全解耦,高内聚低耦合
3. 领域服务(跨聚合逻辑)
public class OrderDomainService {
// 创建订单,同时校验价格、库存
public Order createOrder(User user, List<Product> products, Address address) {
// 逻辑跨多个聚合,所以放在 DomainService
Order order = OrderFactory.create(user, products, address);
return order;
}
}
4. 应用层(只编排,不写业务)
@Service
public class OrderApplicationService {
private OrderRepository orderRepository;
private OrderDomainService orderDomainService;
// 创建订单
@Transactional
public OrderDto createOrder(CreateOrderCommand cmd) {
User user = userRepository.get(cmd.getUserId());
List<Product> products = productRepository.list(cmd.getProductIds());
Order order = orderDomainService.createOrder(user, products, cmd.getAddress());
orderRepository.save(order);
return OrderConverter.toDto(order);
}
// 支付订单
@Transactional
public void payOrder(PayOrderCommand cmd) {
Order order = orderRepository.get(cmd.getOrderId());
order.pay(); // 领域行为
orderRepository.save(order);
}
}
5. 接口层(Controller)
@RestController
@RequestMapping("/order")
public class OrderController {
private OrderApplicationService orderAppService;
@PostMapping("/create")
public Result<OrderDto> create(@RequestBody CreateOrderRequest req) {
CreateOrderCommand cmd = CommandConverter.toCommand(req);
return Result.success(orderAppService.createOrder(cmd));
}
}
6. 仓储接口(领域只依赖接口,不依赖实现)
public interface OrderRepository {
Order get(OrderId id);
void save(Order order);
}
实现类放在 infrastructure 层,用 MyBatis / JPA 都行。
七、第六步:完整业务流程(DDD 视角跑一遍)
- 前端请求 → Controller
- Controller 转 Command → 应用服务
- 应用服务:
- 加载聚合
- 调用聚合根方法(真正业务逻辑)
- 保存聚合
- 事务提交
- 聚合内部发布领域事件
- 其他微服务监听事件执行自己逻辑
整个系统做到:
- 业务逻辑高度内聚
- 状态安全可控
- 扩展极强,加逻辑只加事件
- 微服务之间高度解耦
八、最后给你一张 DDD 终极总结图(电商版)
电商 Domain
├─ 核心域:订单上下文
│ └─ 聚合:Order(AR) → OrderItem、Address、Amount
├─ 支撑域:商品库存上下文
├─ 支撑域:物流上下文
└─ 通用域:用户、支付上下文
领域模型内部
聚合根 → 实体 → 值对象
领域服务 → 处理跨聚合逻辑
领域事件 → 跨服务通信
架构
接口层 → 应用层 → 领域层 → 基础设施层
本文来自博客园,作者:蓝迷梦,转载请注明原文链接:https://www.cnblogs.com/hewei-blogs/articles/19811261

浙公网安备 33010602011771号