.NET领域驱动设计(DDD)落地全攻略:从核心编码到微服务重构实战
.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 上下文映射与服务通信
上下文之间避免直接依赖数据库,通过以下方式通信:
-
领域事件:异步通信,解耦上下游,比如订单付款通知库存、物流。
-
API网关:同步调用,获取基础数据,比如订单服务调用用户服务获取用户信息。
-
共享内核:通用公共组件,比如枚举、值对象、公共DTO,避免重复定义。
六、实战案例:从单体到DDD微服务重构
接下来通过实际重构流程,讲解传统单体订单模块如何改造为DDD架构,再拆分为独立微服务。
6.1 传统单体架构痛点
-
订单业务逻辑全部写在OrderService中,方法长达几百行,包含创建、付款、取消、售后等逻辑,难以维护。
-
控制器直接调用Service,Service直接操作EF Core,业务逻辑与数据访问耦合。
-
新增业务逻辑直接修改原有方法,违背开闭原则,容易引发bug。
-
无法拆分微服务,所有业务共用一个数据库,耦合度极高。
6.2 第一步:分层重构,剥离领域层
按照DDD分层架构拆分项目,严格遵循层级依赖关系:
标准DDD分层:表示层(Controller) -> 应用层(Application,业务流程编排) -> 领域层(Domain,核心业务逻辑) -> 基础设施层(Infrastructure,数据访问、外部服务)
重构动作:
-
剥离Service中的业务逻辑,封装到聚合根内部,领域层只保留纯业务代码,无任何框架依赖。
-
抽取仓储接口与工作单元,数据访问逻辑下沉到基础设施层。
-
拆分领域事件,将耦合的业务逻辑拆分为独立事件处理器。
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 第三步:按限界上下文拆分微服务
-
将订单领域相关代码、仓储、应用服务独立为OrderService微服务。
-
商品、库存相关代码独立为GoodsService微服务。
-
引入消息队列(RabbitMQ)替代本地MediatR,实现跨服务领域事件通信。
-
每个微服务独立数据库,实现数据隔离,彻底解耦。
-
通过API网关统一对外提供接口,实现服务治理。
6.5 重构后优势
-
业务逻辑高度内聚,领域层代码可复用、易测试。
-
新增业务只需新增事件处理器,不修改原有代码,符合开闭原则。
-
微服务边界清晰,基于业务域拆分,后续扩展方便。
-
层级职责明确,便于团队协作开发,降低维护成本。
七、DDD落地避坑指南
-
避免过度设计:简单业务无需强行用DDD,中小项目可先采用精简版DDD,复杂业务再完整落地。
-
领域层禁止依赖框架:领域层不能引用EF Core、MediatR等框架,保证纯业务性。
-
聚合粒度不宜过大:聚合过大导致事务性能差,过小则失去一致性保护,按业务边界划分。
-
禁止跨聚合直接修改数据:通过领域事件实现跨聚合通信。
-
先战略后战术:先划分限界上下文,再写领域代码,避免先编码后拆分。
八、总结
DDD在.NET中的落地,核心是战略设计划边界,战术设计写代码,从聚合根、值对象、领域事件等核心模块入手,结合MediatR实现事件解耦,通过EF Core落地仓储与工作单元,最终基于限界上下文完成微服务拆分。
相比于传统架构,DDD虽然前期编码成本稍高,但长期来看,能极大提升复杂业务系统的可维护性、可扩展性,尤其适合中大型企业级应用和微服务架构。建议开发者从简单模块开始尝试落地,逐步吃透DDD思想,真正让架构服务于业务。
📌 原创不易,觉得有用的话欢迎点赞、收藏、关注,后续会更新更多.NET DDD实战源码和微服务进阶内容!
💬 评论区可留言交流DDD落地遇到的问题,会逐一回复~
更多推荐
所有评论(0)