Comprehensive Guide to Domain-Driven Design (DDD)
Comprehensive Guide to Domain-Driven Design (DDD)
https://jdaniel1987.github.io/DomainDrivenDesign
这是一篇基于你提供的网页内容整理的博客文章。为了便于读者理解,我重新梳理了结构,并对关键代码和概念进行了重点标注。
🚀 全面指南:领域驱动设计 (DDD) 核心与进阶
作者:Jaime Daniel Delgado Ortega
发布日期:2024年9月23日
领域驱动设计(DDD)是一种以业务领域为核心的软件开发战略方法。它通过将软件模型与业务概念对齐,提供了一套管理复杂性的工具。
本文将深入探讨 DDD 的核心概念与进阶模式,并结合 .NET 代码示例,展示如何在项目中应用这些理念。
🧩 DDD 的核心概念
1. 通用语言 (Ubiquitous Language)
通用语言是开发人员和领域专家(如产品经理)之间共享的词汇表。它确保所有参与者对业务概念的理解保持一致。
- 示例:在代码和业务沟通中,“客户”指代同一对象,“订单状态”的生命周期(待处理、已发货、已送达)在系统中是统一的。
2. 实体 (Entities)
实体是拥有唯一标识(ID)的对象,其身份在生命周期中保持不变。
public class OrderItem
{
public required Guid Id { get; set; } // 唯一标识
public required string ProductName { get; set; }
public required decimal Price { get; set; }
public required int Quantity { get; set; }
}
3. 值对象 (Value Objects)
值对象没有唯一标识,它们通过属性值来定义。如果两个值对象的所有属性都相同,则视为相等。值对象通常是不可变的。
代码示例:
Price作为一个值对象,通过record实现不可变性,并重载了运算符。
public record Price
{
public decimal Value { get; init; }
private Price(decimal value)
{
if (value < 0) throw new ArgumentException("Price cannot be negative");
Value = value;
}
public static Price Create(decimal value) => new(value);
// 重载运算符,支持价格直接相加
public static Price operator +(Price a, Price b) => new Price(a.Value + b.Value);
}
4. 聚合 (Aggregates)
聚合是一组相关对象的集合,作为一个整体被对待。聚合根(Aggregate Root)负责维护聚合内部的一致性。
public class Order
{
public required Guid Id { get; set; }
public required DateTime OrderDate { get; set; }
public required Customer Customer { get; set; }
public required List<OrderItem> Items { get; set; }
}
5. 仓储 (Repositories)
仓储充当聚合根集合的抽象,隔离了领域模型与底层数据访问逻辑。
public interface IOrderRepository
{
Task<Order> GetById(int id);
Task Save(Order order);
}
6. 领域服务 (Domain Services)
当某些领域逻辑不适合放在实体或值对象中时(例如涉及多个聚合的操作),我们将其封装在领域服务中。
public class PaymentService
{
public bool ProcessPayment(Order order, PaymentDetails paymentDetails)
{
// 处理支付的核心领域逻辑
}
}
🚀 进阶 DDD 概念
1. 限界上下文 (Bounded Contexts)
限界上下文定义了特定领域模型的边界。同一个概念(如“订单”)在不同的上下文中可能有不同的定义和属性。
- 销售上下文 (Sales Context):订单包含客户和支付信息。
- 发货上下文 (Shipping Context):订单仅包含发货地址和追踪号。
2. 领域事件 (Domain Events)
领域事件用于通知系统业务逻辑中发生了重要变化,常用于解耦。
// 定义事件
public record OrderPlacedEvent(int OrderId, DateTime PlacedOn) : IDomainEvent;
// 处理事件
public class OrderPlacedHandler : IEventHandler<OrderPlacedEvent>
{
public Task Handle(OrderPlacedEvent domainEvent)
{
// 处理订单创建后的逻辑,如发送通知
}
}
3. 工厂 (Factories)
工厂用于封装复杂聚合的创建逻辑,确保聚合在创建时即满足所有业务规则。
public class OrderFactory
{
public static Order CreateOrder(Customer customer, List<OrderItem> items)
{
var order = new Order() { CustomerId = customer.Id, /*...*/ };
foreach (var item in items) order.AddItem(item);
return order;
}
}
4. 事件溯源 (Event Sourcing)
事件溯源不直接存储对象的当前状态,而是存储导致状态变化的一系列事件。通过重放事件来重建对象状态。
public class Order
{
public OrderStatus Status { get; set; }
private List<IDomainEvent> _events = new List<IDomainEvent>();
public void Apply(OrderPlacedEvent @event)
{
this.Status = OrderStatus.Placed; // 根据事件更新状态
_events.Add(@event); // 记录事件
}
}
5. 防腐层 (Anti-Corruption Layer, ACL)
ACL 用于保护当前领域的模型不受外部系统模型的影响,通过转换层将外部模型翻译为内部领域模型。
public class ExternalCustomerAdapter
{
public Customer Convert(ExternalCustomer externalCustomer)
{
// 将外部客户模型转换为内部领域客户模型
}
}
6. 规约 (Specifications)
规约模式将业务规则封装为可重用的对象,用于验证对象或过滤集合。
public class EligibleForDiscountSpecification : ISpecification<Order>
{
public bool IsSatisfiedBy(Order order)
{
return order.TotalAmount > 100; // 满100元符合折扣条件
}
}
7. 策略 (Policies)
策略封装了可能影响系统不同部分的复杂业务规则,通常响应领域事件执行。
public class DiscountPolicy
{
public decimal ApplyDiscount(Order order)
{
if (order.TotalAmount > 500) return order.TotalAmount * 0.10m; // 满500打9折
return 0;
}
}
⚖️ DDD 的利与弊
✅ 优势
- 清晰性:软件设计与业务高度对齐,直观易懂。
- 可扩展性:通过限界上下文、CQRS 和事件溯源,系统能更好地应对复杂度和性能扩展。
- 灵活性:更容易适应业务领域的变更。
⚠️ 挑战
- 学习曲线:概念较多,团队上手难度大。
- 复杂性:对于简单项目,引入 DDD 可能导致过度设计。
🔗 DDD、整洁架构与 CQRS 的关系
这三者是互补的架构方法:
- DDD:专注于业务建模,确保核心逻辑反映真实业务。
- 整洁架构:强调关注点分离,将业务逻辑置于中心,独立于框架和数据库。
- CQRS:分离读写操作,配合 DDD 的限界上下文,使复杂业务操作的设计更加聚焦。
总结:DDD 提供了一套系统化的方法来管理软件复杂性。虽然它引入了额外的复杂性,但对于大型、复杂的业务系统,其带来的长期可维护性和业务对齐价值是巨大的。
本文基于 Eric Evans 的经典著作《领域驱动设计:软件核心复杂性应对之道》整理。

浙公网安备 33010602011771号