aspnetboilerplate 领域驱动设计案例:电商平台订单系统实现

aspnetboilerplate 领域驱动设计案例:电商平台订单系统实现

【免费下载链接】aspnetboilerplate aspnetboilerplate: 是一个开源的 ASP.NET Core 应用程序框架,提供了各种开箱即用的功能和模块,方便开发者构建可扩展和可维护的 Web 应用程序。适合开发者使用 ASP.NET Core 构建企业级 Web 应用程序。 【免费下载链接】aspnetboilerplate 项目地址: https://gitcode.com/gh_mirrors/as/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 类将作为聚合根,包含订单项、配送地址等子实体,并通过仓储直接访问。

aspnetboilerplate 分层架构

图 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 订单系统,关键收益包括:

  1. 业务逻辑内聚:订单状态流转、库存检查等核心逻辑封装在聚合根和领域服务中,避免业务规则散落在控制器或工具类。

  2. 可测试性提升:领域模型不依赖框架,可通过单元测试验证业务规则,如 test/Abp.Tests/ 目录下的测试案例。

  3. 系统扩展性增强:通过领域事件解耦订单创建、支付、物流等流程,新增业务功能只需添加事件处理器。

建议在实际项目中:

  • 严格控制聚合根大小,避免超大聚合导致性能问题
  • 领域服务仅包含跨聚合业务逻辑,避免业务逻辑泄露到应用层
  • 使用 UnitOfWork 管理事务边界,确保数据一致性

完整订单系统实现可参考官方文档 Domain-Services.mdEntities.md,更多领域驱动设计实践可关注项目 README.md 中的架构指南。

【免费下载链接】aspnetboilerplate aspnetboilerplate: 是一个开源的 ASP.NET Core 应用程序框架,提供了各种开箱即用的功能和模块,方便开发者构建可扩展和可维护的 Web 应用程序。适合开发者使用 ASP.NET Core 构建企业级 Web 应用程序。 【免费下载链接】aspnetboilerplate 项目地址: https://gitcode.com/gh_mirrors/as/aspnetboilerplate

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值