领域驱动设计在.NET中的实践
领域驱动设计(Domain-Driven Design, DDD)是一种软件开发方法,它强调将业务领域作为软件设计的核心。下面我将介绍如何在.NET中应用DDD,并提供相应的代码示例。
核心概念
1. 领域模型 (Domain Model)
public class Order
{
public int Id { get; private set; }
public DateTime OrderDate { get; private set; }
public Address ShippingAddress { get; private set; }
private readonly List<OrderItem> _items = new();
public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly();
public Order(Address shippingAddress)
{
OrderDate = DateTime.UtcNow;
ShippingAddress = shippingAddress ?? throw new ArgumentNullException(nameof(shippingAddress));
}
public void AddItem(Product product, int quantity)
{
if (product == null) throw new ArgumentNullException(nameof(product));
if (quantity <= 0) throw new ArgumentException("Quantity must be positive");
var item = new OrderItem(product, quantity);
_items.Add(item);
}
}
2. 值对象 (Value Object)
public class Address : ValueObject
{
public string Street { get; }
public string City { get; }
public string ZipCode { get; }
public Address(string street, string city, string zipCode)
{
Street = street;
City = city;
ZipCode = zipCode;
}
protected override IEnumerable<object> GetEqualityComponents()
{
yield return Street;
yield return City;
yield return ZipCode;
}
}
3. 仓储模式 (Repository Pattern)
public interface IOrderRepository
{
Task<Order> GetByIdAsync(int orderId);
Task AddAsync(Order order);
Task UpdateAsync(Order order);
}
public class OrderRepository : IOrderRepository
{
private readonly OrderDbContext _context;
public OrderRepository(OrderDbContext context)
{
_context = context;
}
public async Task<Order> GetByIdAsync(int orderId)
{
return await _context.Orders
.Include(o => o.Items)
.FirstOrDefaultAsync(o => o.Id == orderId);
}
public async Task AddAsync(Order order)
{
await _context.Orders.AddAsync(order);
await _context.SaveChangesAsync();
}
public async Task UpdateAsync(Order order)
{
_context.Orders.Update(order);
await _context.SaveChangesAsync();
}
}
4. 领域服务 (Domain Service)
public class OrderService
{
private readonly IOrderRepository _orderRepository;
private readonly IProductRepository _productRepository;
public OrderService(
IOrderRepository orderRepository,
IProductRepository productRepository)
{
_orderRepository = orderRepository;
_productRepository = productRepository;
}
public async Task PlaceOrder(int customerId, Address shippingAddress, Dictionary<int, int> productQuantities)
{
var order = new Order(shippingAddress);
foreach (var (productId, quantity) in productQuantities)
{
var product = await _productRepository.GetByIdAsync(productId);
order.AddItem(product, quantity);
}
await _orderRepository.AddAsync(order);
}
}
5. 应用层 (Application Layer)
public class OrderApplicationService
{
private readonly OrderService _orderService;
private readonly IOrderRepository _orderRepository;
public OrderApplicationService(
OrderService orderService,
IOrderRepository orderRepository)
{
_orderService = orderService;
_orderRepository = orderRepository;
}
public async Task<OrderDto> PlaceOrder(PlaceOrderDto dto)
{
var shippingAddress = new Address(
dto.Street,
dto.City,
dto.ZipCode);
await _orderService.PlaceOrder(
dto.CustomerId,
shippingAddress,
dto.ProductQuantities);
// 返回订单结果
var order = await _orderRepository.GetLatestForCustomerAsync(dto.CustomerId);
return OrderDto.FromOrder(order);
}
}
在.NET中实现DDD的最佳实践
-
使用清晰的层次结构:
- 应用层 (Application)
- 领域层 (Domain)
- 基础设施层 (Infrastructure)
- 表现层 (Presentation/API)
-
使用MediatR实现CQRS模式:
public class CreateOrderCommand : IRequest<OrderDto>
{
public int CustomerId { get; set; }
public AddressDto ShippingAddress { get; set; }
public Dictionary<int, int> ProductQuantities { get; set; }
}
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, OrderDto>
{
private readonly OrderService _orderService;
public CreateOrderCommandHandler(OrderService orderService)
{
_orderService = orderService;
}
public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
var address = new Address(
request.ShippingAddress.Street,
request.ShippingAddress.City,
request.ShippingAddress.ZipCode);
await _orderService.PlaceOrder(
request.CustomerId,
address,
request.ProductQuantities);
// 返回订单DTO
}
}
- 使用领域事件 (Domain Events):
public interface IDomainEvent
{
DateTime OccurredOn { get; }
}
public class OrderPlacedEvent : IDomainEvent
{
public int OrderId { get; }
public int CustomerId { get; }
public DateTime OccurredOn { get; } = DateTime.UtcNow;
public OrderPlacedEvent(int orderId, int customerId)
{
OrderId = orderId;
CustomerId = customerId;
}
}
// 在领域模型中触发事件
public class Order
{
private readonly List<IDomainEvent> _domainEvents = new();
public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
public void ClearDomainEvents() => _domainEvents.Clear();
private void AddDomainEvent(IDomainEvent domainEvent) => _domainEvents.Add(domainEvent);
// 在适当的时候触发事件
private void OnOrderPlaced()
{
AddDomainEvent(new OrderPlacedEvent(Id, CustomerId));
}
}
总结
在.NET中实施DDD需要:
- 专注于核心领域和领域逻辑
- 使用清晰的架构分层
- 正确区分实体、值对象和聚合根
- 使用仓储模式隔离持久化逻辑
- 通过领域服务处理跨聚合的业务逻辑
- 使用领域事件处理领域内的副作用
通过遵循这些原则,可以构建出更可维护、更符合业务需求的.NET应用程序。
Leave a Reply