w4n9hu1's Blog

搭建一个DDD分层架构项目

1 主要概念:业务逻辑,领域逻辑

DDD聚焦于业务,业务逻辑落地到代码,主要关注两层,Application Layer和Domain Layer。 分别对应着测试用例(use cases)和领域逻辑(domian logic)。

business_logic

2 DDD概念在分层架构中的落地

architecture

虚线箭头和理论有所区别,因为webapi层实际在解决方案中还承担着host的作用。

Domain

  • Aggregate & Aggregate Root 聚合与聚合根
  • Entity 实体
  • Value Object 值对象
  • Repository Interface 仓储接口
  • Domain Service 领域服务
  • Domain Event 领域事件
  • Factory 工厂

主要的业务逻辑,包括实体,值对象,仓储接口等,复杂逻辑可添加领域服务。

public class Order : IAggregateRoot
{
    public int OrderId { get; set; }

    public string OrderCode { get; set; }

    internal Order(string orderCode)
    {
        if (string.IsNullOrEmpty(orderCode))
        {
            throw new OrderException("OrderCode should have value!");
        }

        OrderCode = orderCode;
    }

    public Weight Weight { get; set; }

    public List<OrderItem> OrderItems { get; set; } = new List<OrderItem>();

    public int TotalAmount
    {
        get
        {
            return OrderItems.Sum(i => i.Amount);
        }
    }

    public int CreatedBy { get; set; }

    public DateTimeOffset CreatedTime { get; set; } = DateTimeOffset.Now;

    public void AddOrderItem(OrderItem orderItem)
    {
        OrderItems.Add(orderItem);
    }
}

Application

  • Application Service 应用服务
  • Data Transfer Object (DTO)
  • Unit of Work 工作单元

包含Contracts,Mappers和Application services,基于Domain层实现用例逻辑。

public async Task<OrderDto> CreateAsync(OrderCreationDto orderCreationDto)
{
    var order = await orderManager.CreateAsync(orderCreationDto.OrderCode);
    order.OrderItems = orderCreationDto.OrderItems.Select(i => new Domain.Order.OrderItem(i.CommodityId, i.CommodityName, i.Amount)).ToList();
    order.CreatedBy = orderCreationDto.CreatedBy;
    order.Weight = orderCreationDto.Weight;

    var savedOrder = await _orderRepository.InsertAsync(order);
    order.OrderId = savedOrder.OrderId;

    await distributedEventBus.PublishAsync(_mapper.Map<OrderChangedEto>(order));
    return _mapper.Map<OrderDto>(order);
}

Use auto object mapping only for Entity to output DTO mappings.

Infrastructure

  • Repository 仓储服务

接口的具体实现,比如持久化数据库,发送Event等。

public class KafkaEventBus : IDistributedEventBus
{
    public Task PublishAsync<T>(T message)
    {
        throw new NotImplementedException();
    }
}

API

  • Controller

很薄的一层,主要负责依赖注入和控制器,使用Dto和Application层交互。

[HttpPost]
public async Task<ActionResult> CreateAsync(OrderCreationDto orderCreationDto)
{
    _logger.LogInformation("Create {@orderCreationDto}", orderCreationDto);
    await _orderService.CreateAsync(orderCreationDto);
    return Ok();
}

3 典型的解决方案文件结构

📁 API
    OrderController

📁 Application
    OrderService / CreateOrderCommandHandler
    📁 DomainEventHandlers
        CreateOrderDomainEventHandler

📁 Contracts
    CreateOrderRequest / CreateOrderCommand
    CreateOrderResponse

📁 Domain
    📁 OrderAggregate
        Order: Entity, IAggregateRoot
        OrderItem
        OrderManager
        Address : ValueObject
        IOrderRepository
    📁 Events
        OrderCreatedDomainEvent
    📁 Exception
        OrderDomainException
    📁 SeedWork
        Entity
        ValueObject
        IAggregateRoot

📁 Domain.Shared
    📁 Enums
        OrderType

📁 Infrastructure
    📁 Repositories
        OrderRepository
  1. Implementing Domain Driven Design - ABP
  2. 领域驱动设计
  3. 实现领域驱动设计