











https://jdaniel1987.github.io/DomainDrivenDesign
这是一篇基于你提供的网页内容整理的博客文章。为了便于读者理解,我重新梳理了结构,并对关键代码和概念进行了重点标注。
作者:Jaime Daniel Delgado Ortega
发布日期:2024年9月23日
领域驱动设计(DDD)是一种以业务领域为核心的软件开发战略方法。它通过将软件模型与业务概念对齐,提供了一套管理复杂性的工具。
本文将深入探讨 DDD 的核心概念与进阶模式,并结合 .NET 代码示例,展示如何在项目中应用这些理念。
通用语言是开发人员和领域专家(如产品经理)之间共享的词汇表。它确保所有参与者对业务概念的理解保持一致。
实体是拥有唯一标识(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; }
}
值对象没有唯一标识,它们通过属性值来定义。如果两个值对象的所有属性都相同,则视为相等。值对象通常是不可变的。
代码示例:
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);
}
聚合是一组相关对象的集合,作为一个整体被对待。聚合根(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; }
}
仓储充当聚合根集合的抽象,隔离了领域模型与底层数据访问逻辑。
public interface IOrderRepository
{
Task<Order> GetById(int id);
Task Save(Order order);
}
当某些领域逻辑不适合放在实体或值对象中时(例如涉及多个聚合的操作),我们将其封装在领域服务中。
public class PaymentService
{
public bool ProcessPayment(Order order, PaymentDetails paymentDetails)
{
// 处理支付的核心领域逻辑
}
}
限界上下文定义了特定领域模型的边界。同一个概念(如“订单”)在不同的上下文中可能有不同的定义和属性。
领域事件用于通知系统业务逻辑中发生了重要变化,常用于解耦。
// 定义事件
public record OrderPlacedEvent(int OrderId, DateTime PlacedOn) : IDomainEvent;
// 处理事件
public class OrderPlacedHandler : IEventHandler<OrderPlacedEvent>
{
public Task Handle(OrderPlacedEvent domainEvent)
{
// 处理订单创建后的逻辑,如发送通知
}
}
工厂用于封装复杂聚合的创建逻辑,确保聚合在创建时即满足所有业务规则。
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;
}
}
事件溯源不直接存储对象的当前状态,而是存储导致状态变化的一系列事件。通过重放事件来重建对象状态。
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); // 记录事件
}
}
ACL 用于保护当前领域的模型不受外部系统模型的影响,通过转换层将外部模型翻译为内部领域模型。
public class ExternalCustomerAdapter
{
public Customer Convert(ExternalCustomer externalCustomer)
{
// 将外部客户模型转换为内部领域客户模型
}
}
规约模式将业务规则封装为可重用的对象,用于验证对象或过滤集合。
public class EligibleForDiscountSpecification : ISpecification<Order>
{
public bool IsSatisfiedBy(Order order)
{
return order.TotalAmount > 100; // 满100元符合折扣条件
}
}
策略封装了可能影响系统不同部分的复杂业务规则,通常响应领域事件执行。
public class DiscountPolicy
{
public decimal ApplyDiscount(Order order)
{
if (order.TotalAmount > 500) return order.TotalAmount * 0.10m; // 满500打9折
return 0;
}
}
这三者是互补的架构方法:
总结:DDD 提供了一套系统化的方法来管理软件复杂性。虽然它引入了额外的复杂性,但对于大型、复杂的业务系统,其带来的长期可维护性和业务对齐价值是巨大的。
本文基于 Eric Evans 的经典著作《领域驱动设计:软件核心复杂性应对之道》整理。
出处:http://www.cnblogs.com/lightsong/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。