aspnetboilerplate 领域驱动设计案例:电商平台订单系统实现
在电商平台开发中,订单系统是核心业务模块,需要处理商品选择、库存锁定、支付流程、物流跟踪等复杂业务逻辑。采用领域驱动设计(DDD)可以有效拆解业务复杂度,提升代码可维护性。本文基于 aspnetboilerplate 框架,通过电商订单系统实现案例,展示如何运用 DDD 思想设计领域模型、领域服务和聚合根。
领域驱动设计核心概念与框架支持
aspnetboilerplate 框架提供了完整的 DDD 基础设施,包括实体基类、领域服务接口、仓储模式等。核心组件位于 src/Abp/Domain/ 目录下,其中:
-
实体(Entity):所有业务对象的基类,实现了主键管理和相等性比较逻辑。如 src/Abp/Domain/Entities/Entity.cs 定义了
Entity<TPrimaryKey>抽象类,提供Id属性和IsTransient()方法判断实体是否为新创建。 -
领域服务(Domain Service):封装跨实体的业务逻辑,需实现 src/Abp/Domain/Services/IDomainService.cs 接口。框架提供的 DomainService 基类集成了依赖注入功能,可直接注入仓储或其他服务。
-
聚合根(Aggregate Root):作为领域模型的边界,订单系统中的
Order类将作为聚合根,包含订单项、配送地址等子实体,并通过仓储直接访问。
图 1:aspnetboilerplate 的分层架构,领域层位于核心位置,与基础设施层通过依赖注入解耦
订单系统领域模型设计
聚合根:订单(Order)
订单聚合根包含订单状态流转、订单项管理、价格计算等核心业务逻辑。继承自 Entity<long> 实现主键管理,同时实现 IAggregateRoot 接口标识聚合根身份:
public class Order : Entity<long>, IAggregateRoot
{
public virtual long UserId { get; protected set; }
public virtual OrderStatus Status { get; protected set; }
public virtual DateTime CreationTime { get; protected set; }
public virtual List<OrderItem> Items { get; protected set; }
public virtual Address ShippingAddress { get; protected set; }
// 领域行为:添加订单项
public virtual void AddItem(Product product, int quantity)
{
if (Status != OrderStatus.Draft)
throw new AbpException("只能向草稿状态的订单添加商品");
var existingItem = Items.FirstOrDefault(i => i.ProductId == product.Id);
if (existingItem != null)
{
existingItem.IncreaseQuantity(quantity);
}
else
{
Items.Add(new OrderItem(this, product.Id, product.Name, product.Price, quantity));
}
}
// 领域行为:更改订单状态
public virtual void ChangeStatus(OrderStatus newStatus)
{
// 状态流转规则校验
if (!IsValidStatusTransition(Status, newStatus))
throw new AbpException($"订单状态从 {Status} 到 {newStatus} 的转换不允许");
Status = newStatus;
// 发布领域事件
DomainEvents.Add(new OrderStatusChangedEvent(this, newStatus));
}
}
子实体:订单项(OrderItem)
订单项作为订单的子实体,依赖订单存在,包含商品ID、名称、单价和数量等属性:
public class OrderItem : Entity<long>
{
public virtual long OrderId { get; protected set; }
public virtual long ProductId { get; protected set; }
public virtual string ProductName { get; protected set; }
public virtual decimal UnitPrice { get; protected set; }
public virtual int Quantity { get; protected set; }
// 子实体必须通过聚合根创建,避免独立存在
protected OrderItem() { }
public OrderItem(Order order, long productId, string productName, decimal unitPrice, int quantity)
{
OrderId = order.Id;
ProductId = productId;
ProductName = productName;
UnitPrice = unitPrice;
Quantity = quantity;
}
// 领域行为:增加数量
public virtual void IncreaseQuantity(int amount)
{
if (amount <= 0)
throw new ArgumentException("数量必须为正数");
Quantity += amount;
}
}
值对象:地址(Address)
配送地址作为值对象,不具备独立生命周期,通过属性组合表达一个完整概念:
public class Address : ValueObject<Address>
{
public virtual string Province { get; protected set; }
public virtual string City { get; protected set; }
public virtual string District { get; protected set; }
public virtual string Street { get; protected set; }
public virtual string ZipCode { get; protected set; }
public virtual string ReceiverName { get; protected set; }
public virtual string PhoneNumber { get; protected set; }
public Address(string province, string city, string district, string street,
string zipCode, string receiverName, string phoneNumber)
{
Province = province;
City = city;
District = district;
Street = street;
ZipCode = zipCode;
ReceiverName = receiverName;
PhoneNumber = phoneNumber;
}
// 值对象通过属性比较相等性
protected override IEnumerable<object> GetEqualityComponents()
{
yield return Province;
yield return City;
yield return District;
yield return Street;
yield return ZipCode;
yield return ReceiverName;
yield return PhoneNumber;
}
}
领域服务实现订单业务流程
订单创建涉及库存检查、价格计算等跨聚合操作,需通过领域服务实现。继承自 DomainService 基类,注入 IRepository<Order> 和 IProductInventoryService:
public class OrderManager : DomainService, IOrderManager
{
private readonly IRepository<Order, long> _orderRepository;
private readonly IProductInventoryService _inventoryService;
private readonly IPriceCalculationService _priceService;
public OrderManager(
IRepository<Order, long> orderRepository,
IProductInventoryService inventoryService,
IPriceCalculationService priceService)
{
_orderRepository = orderRepository;
_inventoryService = inventoryService;
_priceService = priceService;
}
// 创建订单领域服务
[UnitOfWork]
public virtual async Task<Order> CreateOrderAsync(long userId, List<OrderItemDto> items, Address shippingAddress)
{
// 1. 检查库存
foreach (var item in items)
{
var inventoryResult = await _inventoryService.CheckInventoryAsync(item.ProductId, item.Quantity);
if (!inventoryResult.Sufficient)
{
throw new AbpException($"商品 {inventoryResult.ProductName} 库存不足");
}
}
// 2. 创建订单聚合根
var order = new Order(userId, OrderStatus.Draft, Clock.Now);
// 3. 添加订单项(调用聚合根行为)
foreach (var item in items)
{
var productPrice = await _priceService.CalculatePriceAsync(item.ProductId, userId);
order.AddItem(new Product(item.ProductId, item.ProductName), item.Quantity);
}
// 4. 设置配送地址
order.SetShippingAddress(shippingAddress);
// 5. 保存订单(仓储操作)
return await _orderRepository.InsertAsync(order);
}
// 订单支付确认领域服务
[UnitOfWork]
public virtual async Task ConfirmPaymentAsync(long orderId, string paymentId)
{
var order = await _orderRepository.GetAsync(orderId);
// 检查订单状态(聚合根状态校验)
if (order.Status != OrderStatus.Draft)
{
throw new AbpException("只能支付草稿状态的订单");
}
// 锁定库存(跨领域服务调用)
foreach (var item in order.Items)
{
await _inventoryService.LockInventoryAsync(item.ProductId, item.Quantity, orderId);
}
// 更新订单状态(调用聚合根行为)
order.ChangeStatus(OrderStatus.Paid);
// 发布支付成功领域事件
DomainEvents.Add(new OrderPaidEvent(order, paymentId));
return order;
}
}
仓储模式与数据持久化
aspnetboilerplate 提供了泛型仓储接口 IRepository,自动实现 CRUD 操作。订单仓储接口继承自 IRepository<Order, long>,如需自定义查询可扩展:
public interface IOrderRepository : IRepository<Order, long>
{
// 自定义查询:获取用户最近订单
Task<List<Order>> GetUserRecentOrdersAsync(long userId, int count = 5);
// 自定义查询:获取待发货订单
Task<List<Order>> GetPendingShipmentOrdersAsync(DateTime? startTime = null);
}
框架自动实现仓储接口,无需编写 SQL。通过 EntityFramework Core 集成 模块,可配置实体映射关系:
public class OrderEntityConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.ToTable("AppOrders");
// 配置聚合根属性
builder.Property(o => o.Status).HasConversion<int>();
builder.Property(o => o.CreationTime).IsRequired();
// 配置子实体集合(聚合根与子实体关系)
builder.OwnsMany(o => o.Items, itemBuilder =>
{
itemBuilder.WithOwner().HasForeignKey("OrderId");
itemBuilder.HasKey("Id");
itemBuilder.Property(i => i.ProductName).HasMaxLength(200).IsRequired();
itemBuilder.Property(i => i.UnitPrice).HasColumnType("decimal(18,2)");
});
// 配置值对象(拥有者关系)
builder.OwnsOne(o => o.ShippingAddress, addressBuilder =>
{
addressBuilder.Property(a => a.Province).HasMaxLength(50).IsRequired();
addressBuilder.Property(a => a.City).HasMaxLength(50).IsRequired();
addressBuilder.Property(a => a.Street).HasMaxLength(200).IsRequired();
addressBuilder.Property(a => a.PhoneNumber).HasMaxLength(20).IsRequired();
});
}
}
领域事件与业务解耦
订单状态变更等关键业务操作会触发领域事件,通过 IDomainEvent 接口实现:
// 订单支付成功领域事件
public class OrderPaidEvent : DomainEvent
{
public Order Order { get; }
public string PaymentId { get; }
public OrderPaidEvent(Order order, string paymentId)
{
Order = order;
PaymentId = paymentId;
}
}
// 事件处理器:发送订单确认邮件
public class OrderPaidEventHandler : IEventHandler<OrderPaidEvent>, ITransientDependency
{
private readonly IEmailSender _emailSender;
private readonly IUserRepository _userRepository;
public OrderPaidEventHandler(IEmailSender emailSender, IUserRepository userRepository)
{
_emailSender = emailSender;
_userRepository = userRepository;
}
public async Task HandleEventAsync(OrderPaidEvent eventData)
{
var user = await _userRepository.GetAsync(eventData.Order.UserId);
await _emailSender.SendAsync(
to: user.EmailAddress,
subject: $"订单 {eventData.Order.Id} 支付成功",
body: $"您的订单 {eventData.Order.Id} 已支付,订单金额:{eventData.Order.TotalAmount:C}"
);
}
}
应用层实现与前端交互
应用层通过 Application Service 封装领域逻辑,提供 REST API 接口:
[AbpAuthorize]
public class OrderAppService : ApplicationService, IOrderAppService
{
private readonly IOrderManager _orderManager;
private readonly IRepository<Order, long> _orderRepository;
public OrderAppService(IOrderManager orderManager, IRepository<Order, long> orderRepository)
{
_orderManager = orderManager;
_orderRepository = orderRepository;
}
[HttpPost]
public async Task<OrderDto> CreateOrderAsync(CreateOrderInput input)
{
// 1. DTO 转换为领域对象
var address = new Address(
input.ShippingAddress.Province,
input.ShippingAddress.City,
input.ShippingAddress.District,
input.ShippingAddress.Street,
input.ShippingAddress.ZipCode,
input.ShippingAddress.ReceiverName,
input.ShippingAddress.PhoneNumber
);
// 2. 调用领域服务
var order = await _orderManager.CreateOrderAsync(
AbpSession.GetUserId(),
input.Items,
address
);
// 3. 领域对象转换为 DTO 返回
return ObjectMapper.Map<OrderDto>(order);
}
}
总结与最佳实践
通过 aspnetboilerplate 框架实现 DDD 订单系统,关键收益包括:
-
业务逻辑内聚:订单状态流转、库存检查等核心逻辑封装在聚合根和领域服务中,避免业务规则散落在控制器或工具类。
-
可测试性提升:领域模型不依赖框架,可通过单元测试验证业务规则,如 test/Abp.Tests/ 目录下的测试案例。
-
系统扩展性增强:通过领域事件解耦订单创建、支付、物流等流程,新增业务功能只需添加事件处理器。
建议在实际项目中:
- 严格控制聚合根大小,避免超大聚合导致性能问题
- 领域服务仅包含跨聚合业务逻辑,避免业务逻辑泄露到应用层
- 使用 UnitOfWork 管理事务边界,确保数据一致性
完整订单系统实现可参考官方文档 Domain-Services.md 和 Entities.md,更多领域驱动设计实践可关注项目 README.md 中的架构指南。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




