.NET领域驱动设计(DDD)落地全攻略:从核心编码到微服务重构实战

💡 文章前言

在.NET后端开发中,随着业务复杂度不断提升,传统三层架构很容易出现业务逻辑散落在控制器、服务层、数据访问层的问题,导致代码臃肿、复用性差、维护成本极高,后续微服务拆分更是无从下手。领域驱动设计(DDD)作为解决复杂业务问题的核心思想,通过聚焦业务领域本身,拆分边界、梳理逻辑,能彻底解决传统架构的痛点。

很多开发者对DDD的理解停留在理论层面,觉得概念抽象、落地难,尤其是在.NET生态中,不知道如何把聚合根、值对象、领域事件等核心概念转化为可运行的代码。本篇文章将全程基于.NET 6/7/8 + EF Core + MediatR技术栈,从零讲解DDD核心模块代码落地,覆盖仓储模式、工作单元、领域事件解耦、限界上下文划分,最后通过单体重构DDD微服务的实战案例,让大家真正掌握DDD在.NET中的落地方法,吃透从理论到实战的全流程。

适用人群:.NET后端开发者、有微服务拆分需求的工程师、想优化复杂业务架构的技术人员
技术栈:.NET 6+/EF Core 6+/MediatR/C#
核心收获:掌握DDD核心编码规范、学会领域事件解耦、落地仓储+工作单元、掌握微服务限界上下文拆分方法


一、DDD核心基础回顾:先理清概念再落地

在写代码之前,必须先明确DDD核心战术模块的定义,避免代码写偏,这是DDD落地的前提:

  • 值对象(Value Object):无唯一标识,通过属性值判断相等性,不可变,用于描述领域属性,比如订单中的地址、商品规格、金额等,不单独存在,依附于实体或聚合根。

  • 实体(Entity):有唯一标识,具备业务行为和状态,核心是业务逻辑而非数据。

  • 聚合根(Aggregate Root):聚合的入口,是特殊的实体,负责维护聚合内数据一致性,对外只能通过聚合根访问聚合内数据,是DDD数据操作的核心单元。

  • 领域事件(Domain Event):领域内发生的业务事件,用于实现业务逻辑解耦,比如订单创建后触发库存扣减、消息通知,避免业务逻辑耦合在同一个方法内。

  • 仓储模式(Repository):封装数据访问逻辑,隔离领域层与数据访问层,领域层只依赖仓储接口,不关心数据存储细节。

  • 工作单元(Unit Of Work):保证多个仓储操作的事务一致性,实现一次提交、事务统一管控。

  • 限界上下文(Bounded Context):DDD战略设计核心,划分业务边界,每个限界上下文对应一个独立业务模块,是微服务拆分的核心依据。

二、DDD核心模块代码落地:聚合根/值对象/领域事件

这部分是DDD落地的核心,我们直接基于C#编写规范代码,区分领域层与其他层级,严格遵循领域层纯业务逻辑,不依赖任何框架的原则。

2.1 基础抽象类封装:复用实体与聚合根通用逻辑

首先封装实体和聚合根的基类,统一唯一标识、领域事件管理等逻辑,避免重复编码,这是DDD编码的规范做法。

// 领域层 - 核心基类(Domain/Abstractions)
/// <summary>
/// 实体基类
/// </summary>
public abstract class Entity
{
    public Guid Id { get; protected set; }

    protected Entity()
    {
        Id = Guid.NewGuid();
    }

    protected Entity(Guid id)
    {
        Id = id;
    }

    // 实体相等性判断:基于唯一标识
    public override bool Equals(object? obj)
    {
        if (obj is not Entity entity) return false;
        return Id.Equals(entity.Id);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

/// <summary>
/// 聚合根基类,继承实体,管理领域事件
/// </summary>
public abstract class AggregateRoot : Entity
{
    // 领域事件集合
    private readonly List<IDomainEvent> _domainEvents = new();

    // 获取领域事件
    public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();

    // 添加领域事件
    protected void AddDomainEvent(IDomainEvent domainEvent)
    {
        _domainEvents.Add(domainEvent);
    }

    // 清空领域事件
    public void ClearDomainEvents()
    {
        _domainEvents.Clear();
    }
}

/// <summary>
/// 领域事件接口
/// </summary>
public interface IDomainEvent : INotification
{
    DateTime OccurredOn { get; }
}

关键点:聚合根单独管理领域事件,保证事件只在聚合内触发,符合DDD数据一致性原则;领域事件继承MediatR的INotification,为后续事件分发做铺垫。

2.2 值对象编码:不可变、属性相等判断

值对象核心是不可变,通过构造函数初始化属性,不提供set方法,相等性基于所有属性判断,而非标识。以订单地址、金额值对象为例:

// 领域层 - 值对象(Domain/ValueObjects)
/// <summary>
/// 地址值对象
/// </summary>
public class AddressValueObject : ValueObject
{
    public string Province { get; }
    public string City { get; }
    public string District { get; }
    public string DetailAddress { get; }

    // 私有构造函数,禁止外部直接实例化,保证不可变
    private AddressValueObject() { }

    public AddressValueObject(string province, string city, string district, string detailAddress)
    {
        // 领域内校验逻辑
        if (string.IsNullOrWhiteSpace(province)) throw new ArgumentNullException(nameof(province));
        if (string.IsNullOrWhiteSpace(city)) throw new ArgumentNullException(nameof(city));
        
        Province = province;
        City = city;
        District = district;
        DetailAddress = detailAddress;
    }

    // 值对象相等性判断核心:重写获取属性组件方法
    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Province;
        yield return City;
        yield return District;
        yield return DetailAddress;
    }
}

/// <summary>
/// 金额值对象
/// </summary>
public class MoneyValueObject : ValueObject
{
    public decimal Amount { get; }
    public string Currency { get; }

    private MoneyValueObject() { }

    public MoneyValueObject(decimal amount, string currency = "CNY")
    {
        if (amount < 0) throw new ArgumentException("金额不能为负数");
        Amount = amount;
        Currency = currency;
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Amount;
        yield return Currency;
    }
}

// 值对象基类
public abstract class ValueObject
{
    protected abstract IEnumerable<object> GetEqualityComponents();

    public override bool Equals(object? obj)
    {
        if (obj is not ValueObject valueObject || obj.GetType() != GetType()) return false;
        return GetEqualityComponents().SequenceEqual(valueObject.GetEqualityComponents());
    }

    public override int GetHashCode()
    {
        return GetEqualityComponents().Aggregate(1, (current, obj) => current * 23 + (obj?.GetHashCode() ?? 0));
    }
}

2.3 聚合根与领域事件编码

订单聚合根为例,订单是典型的聚合根,包含订单明细、地址、金额等子实体/值对象,订单创建时触发领域事件,实现业务解耦。

// 领域层 - 聚合根(Domain/Aggregates/OrderAggregate)
/// <summary>
/// 订单聚合根
/// </summary>
public class Order : AggregateRoot
{
    // 私有字段,封装状态,禁止外部直接修改
    private readonly List<OrderItem> _orderItems = new();
    public OrderStatus Status { get; private set; }
    public AddressValueObject ReceiveAddress { get; private set; }
    public MoneyValueObject TotalAmount { get; private set; }
    public Guid UserId { get; private set; }
    public DateTime CreateTime { get; private set; }

    // 私有构造,强制通过工厂方法或业务方法创建,保证数据一致性
    private Order() { }

    // 创建订单的业务方法,聚合根核心:业务逻辑封装在自身内部,而非服务层
    public static Order CreateOrder(Guid userId, AddressValueObject receiveAddress, List<OrderItem> orderItems)
    {
        var order = new Order
        {
            Id = Guid.NewGuid(),
            UserId = userId,
            ReceiveAddress = receiveAddress,
            Status = OrderStatus.PendingPayment,
            CreateTime = DateTime.Now,
            TotalAmount = new MoneyValueObject(orderItems.Sum(x => x.TotalPrice.Amount))
        };
        
        // 添加订单明细
        order._orderItems.AddRange(orderItems);
        // 触发订单创建领域事件
        order.AddDomainEvent(new OrderCreatedDomainEvent(order.Id, order.UserId, order.TotalAmount.Amount));
        
        return order;
    }

    // 订单付款完成业务方法
    public void OrderPaySuccess()
    {
        if (Status != OrderStatus.PendingPayment)
            throw new InvalidOperationException("当前订单状态不支持付款操作");
        
        Status = OrderStatus.Paid;
        // 触发订单付款成功事件
        AddDomainEvent(new OrderPaidDomainEvent(Id, UserId, TotalAmount.Amount));
    }

    // 公开只读属性,外部获取订单明细
    public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
}

/// <summary>
/// 订单明细实体(聚合内实体,只能通过聚合根访问)
/// </summary>
public class OrderItem : Entity
{
    public Guid OrderId { get; private set; }
    public Guid GoodsId { get; private set; }
    public string GoodsName { get; private set; }
    public int Count { get; private set; }
    public MoneyValueObject UnitPrice { get; private set; }
    public MoneyValueObject TotalPrice { get; private set; }

    private OrderItem() { }

    public static OrderItem CreateOrderItem(Guid goodsId, string goodsName, int count, decimal unitPrice)
    {
        if (count <= 0) throw new ArgumentException("商品数量必须大于0");
        var unitMoney = new MoneyValueObject(unitPrice);
        return new OrderItem
        {
            GoodsId = goodsId,
            GoodsName = goodsName,
            Count = count,
            UnitPrice = unitMoney,
            TotalPrice = new MoneyValueObject(unitMoney.Amount * count)
        };
    }
}

// 订单状态枚举
public enum OrderStatus
{
    PendingPayment = 0,
    Paid = 1,
    Shipping = 2,
    Completed = 3,
    Canceled = 4
}

// 领域事件实现(Domain/Events)
public record OrderCreatedDomainEvent(Guid OrderId, Guid UserId, decimal TotalAmount) : IDomainEvent
{
    public DateTime OccurredOn { get; } = DateTime.Now;
}

public record OrderPaidDomainEvent(Guid OrderId, Guid UserId, decimal TotalAmount) : IDomainEvent
{
    public DateTime OccurredOn { get; } = DateTime.Now;
}

核心规范:1. 聚合根禁止外部直接修改属性,所有状态变更通过业务方法实现,内置校验逻辑;2. 聚合内实体不对外暴露,只能通过聚合根操作;3. 业务事件统一由聚合根触发,不散落各处。

三、MediatR实现领域事件解耦:事件分发与监听

传统开发中,订单创建后直接在方法内调用库存、通知、日志逻辑,导致强耦合。DDD中通过领域事件 + MediatR实现事件驱动,发布者与订阅者完全解耦,新增业务逻辑只需新增事件处理器,无需修改原有代码。

3.1 注册MediatR与事件发布服务

首先在Program.cs中注册MediatR服务,指定领域事件所在程序集:

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// 注册MediatR,扫描当前程序集的事件处理器
builder.Services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly, typeof(OrderCreatedDomainEvent).Assembly);
});

// 注册领域事件发布器(封装MediatR事件发布,方便工作单元调用)
builder.Services.AddScoped<IDomainEventDispatcher, MediatRDomianEventDispatcher>();

3.2 领域事件分发器封装

// 应用层 - 事件分发器(Application/Common/Dispatchers)
public interface IDomainEventDispatcher
{
    Task DispatchEventsAsync(AggregateRoot aggregateRoot, CancellationToken cancellationToken = default);
}

public class MediatRDomianEventDispatcher : IDomainEventDispatcher
{
    private readonly IMediator _mediator;

    public MediatRDomianEventDispatcher(IMediator mediator)
    {
        _mediator = mediator;
    }

    public async Task DispatchEventsAsync(AggregateRoot aggregateRoot, CancellationToken cancellationToken = default)
    {
        if (aggregateRoot.DomainEvents.Count == 0) return;
        
        // 逐一发布领域事件
        foreach (var domainEvent in aggregateRoot.DomainEvents)
        {
            await _mediator.Publish(domainEvent, cancellationToken);
        }
        
        // 发布后清空事件,避免重复发布
        aggregateRoot.ClearDomainEvents();
    }
}

3.3 领域事件处理器编写

针对订单创建、付款事件,编写独立的处理器,实现库存扣减、消息通知等逻辑,完全解耦:

// 应用层 - 事件处理器(Application/EventHandlers)
/// <summary>
/// 订单创建事件处理器:扣减库存
/// </summary>
public class OrderCreatedDomainEventHandler : INotificationHandler<OrderCreatedDomainEvent>
{
    private readonly IGoodsRepository _goodsRepository;
    private readonly ILogger<OrderCreatedDomainEventHandler> _logger;

    public OrderCreatedDomainEventHandler(IGoodsRepository goodsRepository, ILogger<OrderCreatedDomainEventHandler> logger)
    {
        _goodsRepository = goodsRepository;
        _logger = logger;
    }

    public async Task Handle(OrderCreatedDomainEvent notification, CancellationToken cancellationToken)
    {
        _logger.LogInformation($"订单{notification.OrderId}创建,开始扣减库存");
        // 调用仓储扣减库存,业务逻辑独立封装
        await _goodsRepository.DeductionStockAsync(notification.OrderId, cancellationToken);
    }
}

/// <summary>
/// 订单付款事件处理器:发送通知
/// </summary>
public class OrderPaidDomainEventHandler : INotificationHandler<OrderPaidDomainEvent>
{
    private readonly INotificationService _notificationService;

    public OrderPaidDomainEventHandler(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }

    public async Task Handle(OrderPaidDomainEvent notification, CancellationToken cancellationToken)
    {
        // 发送站内信/短信通知
        await _notificationService.SendPaySuccessNoticeAsync(notification.UserId, notification.OrderId, cancellationToken);
    }
}

四、EF Core实现仓储模式+工作单元:数据访问层规范

DDD中仓储模式负责隔离领域层与数据访问层,工作单元负责事务管控,避免直接在业务层操作EF Core上下文,实现分层解耦。

4.1 仓储接口定义(领域层)

仓储接口定义在领域层,领域层依赖抽象而非具体实现,符合依赖倒置原则:

// 领域层 - 仓储接口(Domain/Repositories)
public interface IOrderRepository
{
    Task<Order?> GetByIdAsync(Guid orderId, CancellationToken cancellationToken = default);
    Task AddAsync(Order order, CancellationToken cancellationToken = default);
    void Update(Order order);
}

public interface IGoodsRepository
{
    Task DeductionStockAsync(Guid orderId, CancellationToken cancellationToken = default);
}

/// <summary>
/// 泛型仓储基接口(复用基础CRUD)
/// </summary>
public interface IRepository<T> where T : AggregateRoot
{
    Task<T?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
    Task AddAsync(T entity, CancellationToken cancellationToken = default);
    void Update(T entity);
    void Delete(T entity);
}

4.2 EF Core仓储实现(基础设施层)

// 基础设施层 - 仓储实现(Infrastructure/Repositories)
public class OrderRepository : IOrderRepository
{
    private readonly AppDbContext _dbContext;

    public OrderRepository(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<Order?> GetByIdAsync(Guid orderId, CancellationToken cancellationToken = default)
    {
        // 包含导航属性查询,保证聚合完整性
        return await _dbContext.Orders
            .Include(x => x.OrderItems)
            .FirstOrDefaultAsync(x => x.Id == orderId, cancellationToken);
    }

    public async Task AddAsync(Order order, CancellationToken cancellationToken = default)
    {
        await _dbContext.Orders.AddAsync(order, cancellationToken);
    }

    public void Update(Order order)
    {
        _dbContext.Orders.Update(order);
    }
}

// 泛型仓储实现
public class Repository<T> : IRepository<T> where T : AggregateRoot
{
    private readonly AppDbContext _dbContext;
    private readonly DbSet<T> _dbSet;

    public Repository(AppDbContext dbContext)
    {
        _dbContext = dbContext;
        _dbSet = dbContext.Set<T>();
    }

    public async Task<T?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
    {
        return await _dbSet.FirstOrDefaultAsync(x => x.Id == id, cancellationToken);
    }

    public async Task AddAsync(T entity, CancellationToken cancellationToken = default)
    {
        await _dbSet.AddAsync(entity, cancellationToken);
    }

    public void Update(T entity)
    {
        _dbSet.Update(entity);
    }

    public void Delete(T entity)
    {
        _dbSet.Remove(entity);
    }
}

4.3 工作单元实现:事务统一管控

工作单元核心是统一提交事务,同时在提交前发布领域事件,保证事务与事件一致性:

// 领域层 - 工作单元接口(Domain/UnitOfWorks)
public interface IUnitOfWork : IDisposable
{
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
    // 获取指定仓储
    IOrderRepository Orders { get; }
    IGoodsRepository Goods { get; }
}

// 基础设施层 - 工作单元实现(Infrastructure/UnitOfWorks)
public class UnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _dbContext;
    private readonly IDomainEventDispatcher _domainEventDispatcher;
    private IOrderRepository? _orderRepository;
    private IGoodsRepository? _goodsRepository;

    public UnitOfWork(AppDbContext dbContext, IDomainEventDispatcher domainEventDispatcher)
    {
        _dbContext = dbContext;
        _domainEventDispatcher = domainEventDispatcher;
    }

    // 懒加载仓储,避免重复实例化
    public IOrderRepository Orders => _orderRepository ??= new OrderRepository(_dbContext);
    public IGoodsRepository Goods => _goodsRepository ??= new GoodsRepository(_dbContext);

    public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        // 1. 执行数据库操作
        var result = await _dbContext.SaveChangesAsync(cancellationToken);
        
        // 2. 获取所有变更的聚合根,发布领域事件
        var aggregateRoots = _dbContext.ChangeTracker
            .Entries<AggregateRoot>()
            .Select(x => x.Entity)
            .Where(x => x.DomainEvents.Any())
            .ToList();
        
        foreach (var root in aggregateRoots)
        {
            await _domainEventDispatcher.DispatchEventsAsync(root, cancellationToken);
        }
        
        return result;
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }
}

4.4 EF Core上下文配置

配置聚合根、值对象的EF Core映射,支持值对象复杂类型映射:

// 基础设施层 - DbContext(Infrastructure/Data)
public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<Order> Orders { get; set; }
    public DbSet<Goods> Goods { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 订单聚合配置
        modelBuilder.Entity<Order>(entity =>
        {
            entity.HasKey(x => x.Id);
            // 值对象配置为复杂类型
            entity.OwnsOne(x => x.ReceiveAddress);
            entity.OwnsOne(x => x.TotalAmount);
            // 一对多配置
            entity.HasMany(x => x.OrderItems)
                .WithOne()
                .HasForeignKey(x => x.OrderId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        // 订单明细配置
        modelBuilder.Entity<OrderItem>(entity =>
        {
            entity.HasKey(x => x.Id);
            entity.OwnsOne(x => x.UnitPrice);
            entity.OwnsOne(x => x.TotalPrice);
        });
    }
}

五、DDD与微服务拆分:限界上下文划分

DDD战略设计的核心是限界上下文,这也是微服务拆分的核心依据,避免盲目拆分导致的分布式事务、服务依赖混乱问题。

5.1 限界上下文划分原则

  • 业务单一性:一个限界上下文只负责一个核心业务域,比如订单域、用户域、商品域、支付域、库存域。

  • 高内聚低耦合:上下文内部业务关联紧密,上下文之间通过领域事件、接口调用通信,不直接依赖数据。

  • 通用语言统一:每个上下文内部术语统一,避免跨域概念混淆。

  • 聚合边界对齐:同一聚合的业务逻辑,归属同一个限界上下文,不跨服务拆分。

5.2 电商业务限界上下文拆分示例

限界上下文 核心业务 对应微服务 通信方式
用户上下文 用户注册、登录、信息管理 用户服务 同步接口调用
商品上下文 商品管理、库存管理 商品服务 领域事件(订单创建扣减库存)
订单上下文 订单创建、状态流转、售后 订单服务 事件发布/订阅
支付上下文 支付、退款、对账 支付服务 事件驱动

5.3 上下文映射与服务通信

上下文之间避免直接依赖数据库,通过以下方式通信:

  1. 领域事件:异步通信,解耦上下游,比如订单付款通知库存、物流。

  2. API网关:同步调用,获取基础数据,比如订单服务调用用户服务获取用户信息。

  3. 共享内核:通用公共组件,比如枚举、值对象、公共DTO,避免重复定义。

六、实战案例:从单体到DDD微服务重构

接下来通过实际重构流程,讲解传统单体订单模块如何改造为DDD架构,再拆分为独立微服务。

6.1 传统单体架构痛点

  • 订单业务逻辑全部写在OrderService中,方法长达几百行,包含创建、付款、取消、售后等逻辑,难以维护。

  • 控制器直接调用Service,Service直接操作EF Core,业务逻辑与数据访问耦合。

  • 新增业务逻辑直接修改原有方法,违背开闭原则,容易引发bug。

  • 无法拆分微服务,所有业务共用一个数据库,耦合度极高。

6.2 第一步:分层重构,剥离领域层

按照DDD分层架构拆分项目,严格遵循层级依赖关系:

标准DDD分层:表示层(Controller) -> 应用层(Application,业务流程编排) -> 领域层(Domain,核心业务逻辑) -> 基础设施层(Infrastructure,数据访问、外部服务)

重构动作:

  1. 剥离Service中的业务逻辑,封装到聚合根内部,领域层只保留纯业务代码,无任何框架依赖。

  2. 抽取仓储接口与工作单元,数据访问逻辑下沉到基础设施层。

  3. 拆分领域事件,将耦合的业务逻辑拆分为独立事件处理器。

6.3 第二步:应用层编写服务(流程编排)

应用层只负责业务流程编排,不包含核心业务逻辑,调用领域层和仓储完成操作:

// 应用层 - 订单服务(Application/Services)
public class OrderAppService
{
    private readonly IUnitOfWork _unitOfWork;

    public OrderAppService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    /// <summary>
    /// 创建订单应用服务
    /// </summary>
    public async Task<Guid> CreateOrderAsync(CreateOrderCommand command, CancellationToken cancellationToken = default)
    {
        // 1. 构建值对象
        var address = new AddressValueObject(command.Province, command.City, command.District, command.DetailAddress);
        
        // 2. 构建订单明细
        var orderItems = command.OrderItems.Select(x => 
            OrderItem.CreateOrderItem(x.GoodsId, x.GoodsName, x.Count, x.UnitPrice)).ToList();
        
        // 3. 调用聚合根方法创建订单(核心业务逻辑在领域层)
        var order = Order.CreateOrder(command.UserId, address, orderItems);
        
        // 4. 仓储添加订单
        await _unitOfWork.Orders.AddAsync(order, cancellationToken);
        
        // 5. 工作单元提交,发布领域事件
        await _unitOfWork.SaveChangesAsync(cancellationToken);
        
        return order.Id;
    }
}

// 命令DTO(Application/Commands)
public record CreateOrderCommand(Guid UserId, string Province, string City, string District, string DetailAddress, List<OrderItemCommand> OrderItems);
public record OrderItemCommand(Guid GoodsId, string GoodsName, int Count, decimal UnitPrice);

6.4 第三步:按限界上下文拆分微服务

  1. 将订单领域相关代码、仓储、应用服务独立为OrderService微服务。

  2. 商品、库存相关代码独立为GoodsService微服务。

  3. 引入消息队列(RabbitMQ)替代本地MediatR,实现跨服务领域事件通信。

  4. 每个微服务独立数据库,实现数据隔离,彻底解耦。

  5. 通过API网关统一对外提供接口,实现服务治理。

6.5 重构后优势

  • 业务逻辑高度内聚,领域层代码可复用、易测试。

  • 新增业务只需新增事件处理器,不修改原有代码,符合开闭原则。

  • 微服务边界清晰,基于业务域拆分,后续扩展方便。

  • 层级职责明确,便于团队协作开发,降低维护成本。

七、DDD落地避坑指南

  • 避免过度设计:简单业务无需强行用DDD,中小项目可先采用精简版DDD,复杂业务再完整落地。

  • 领域层禁止依赖框架:领域层不能引用EF Core、MediatR等框架,保证纯业务性。

  • 聚合粒度不宜过大:聚合过大导致事务性能差,过小则失去一致性保护,按业务边界划分。

  • 禁止跨聚合直接修改数据:通过领域事件实现跨聚合通信。

  • 先战略后战术:先划分限界上下文,再写领域代码,避免先编码后拆分。

八、总结

DDD在.NET中的落地,核心是战略设计划边界,战术设计写代码,从聚合根、值对象、领域事件等核心模块入手,结合MediatR实现事件解耦,通过EF Core落地仓储与工作单元,最终基于限界上下文完成微服务拆分。

相比于传统架构,DDD虽然前期编码成本稍高,但长期来看,能极大提升复杂业务系统的可维护性、可扩展性,尤其适合中大型企业级应用和微服务架构。建议开发者从简单模块开始尝试落地,逐步吃透DDD思想,真正让架构服务于业务。


📌 原创不易,觉得有用的话欢迎点赞、收藏、关注,后续会更新更多.NET DDD实战源码和微服务进阶内容!

💬 评论区可留言交流DDD落地遇到的问题,会逐一回复~

更多推荐