告别面条代码:ASP.NET Core领域驱动设计实战指南
你是否还在为大型ASP.NET Core项目的代码混乱而头疼?业务逻辑与数据访问交织、修改一处牵动全身、新功能迭代举步维艰?本文将带你用领域驱动设计(DDD)重构应用架构,通过分层设计实现代码解耦,让系统随业务增长而优雅演化。读完本文你将掌握:
- 如何在ASP.NET Core中落地DDD分层架构
- 领域模型设计与聚合根实现技巧
- 仓储模式与EF Core的无缝集成
- 完整的DDD项目结构与最佳实践
DDD架构在ASP.NET Core中的应用
领域驱动设计(DDD)是一种通过将业务领域建模为核心来设计复杂软件的方法论。在ASP.NET Core项目中实施DDD,能够有效解决业务复杂度提升带来的代码维护难题。
经典DDD分层架构
DDD推荐的分层架构包括:
- 表现层:处理HTTP请求和响应(Controllers)
- 应用层:协调领域逻辑,不包含业务规则
- 领域层:核心业务逻辑和领域模型
- 基础设施层:提供技术支持,如数据库访问
在ASP.NET Core中,我们可以通过项目结构清晰划分这些层次:
src/
├── YourApp.Web/ # 表现层(Controllers、Pages)
├── YourApp.Application/ # 应用层(Services、DTOs)
├── YourApp.Domain/ # 领域层(Entities、Aggregates)
└── YourApp.Infrastructure/ # 基础设施层(Repositories、EF Core)
ASP.NET Core与DDD的契合点
ASP.NET Core的依赖注入系统为DDD分层架构提供了完美支持。通过构造函数注入,我们可以轻松实现各层之间的解耦。例如,表现层的控制器依赖于应用层服务,应用层服务又依赖于领域层和仓储接口。
领域模型设计实践
领域模型是DDD的核心,它捕获业务概念和规则。在ASP.NET Core中,我们通常使用实体(Entity)和值对象(Value Object)构建领域模型。
实体与聚合根设计
实体是具有唯一标识的领域对象,其生命周期可能跨越多个状态变化。聚合根是聚合的根实体,负责维护聚合内的一致性。
以订单系统为例,Order是典型的聚合根,包含OrderItem子实体:
// 领域层实体示例 [参考 src/Mvc/test/WebSites/BasicWebSite/ContactsRepository.cs]
public class Order : Entity
{
public OrderId Id { get; private set; }
public CustomerId CustomerId { get; private set; }
public OrderStatus Status { get; private set; }
private List<OrderItem> _items = new();
// 业务行为封装
public void AddItem(Product product, int quantity)
{
if (Status != OrderStatus.Draft)
throw new InvalidOperationException("只能向草稿订单添加商品");
_items.Add(new OrderItem(product.Id, quantity, product.Price));
}
// 更多领域行为...
}
值对象实现
值对象是用于描述领域特征的对象,没有唯一标识,通常是不可变的。例如,地址、货币金额等:
public record Address(
string Street,
string City,
string State,
string PostalCode,
string Country
);
public record Money(
decimal Amount,
string CurrencyCode
);
仓储模式与EF Core集成
仓储模式(Repository Pattern)为领域模型提供持久化接口,隔离领域层与数据访问技术。在ASP.NET Core中,我们通常使用EF Core实现仓储接口。
仓储接口设计
在领域层定义仓储接口,保持对具体数据访问技术的无知:
// 领域层仓储接口 [参考 src/Identity/EntityFrameworkCore/src/UserStore.cs]
public interface IOrderRepository
{
Task<Order?> GetByIdAsync(OrderId id, CancellationToken cancellationToken = default);
Task AddAsync(Order order, CancellationToken cancellationToken = default);
Task UpdateAsync(Order order, CancellationToken cancellationToken = default);
Task DeleteAsync(Order order, CancellationToken cancellationToken = default);
}
EF Core实现仓储
在基础设施层实现仓储接口,利用EF Core进行数据访问:
// 基础设施层仓储实现 [参考 src/Identity/EntityFrameworkCore/src/UserStore.cs]
public class EfCoreOrderRepository : IOrderRepository
{
private readonly AppDbContext _dbContext;
public EfCoreOrderRepository(AppDbContext dbContext)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public async Task<Order?> GetByIdAsync(OrderId id, CancellationToken cancellationToken = default)
{
return await _dbContext.Orders
.Include(o => o.Items)
.FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
}
// 其他方法实现...
}
数据库上下文配置
正确配置EF Core的DbContext,映射领域模型到数据库表:
// EF Core上下文配置 [参考 src/Identity/testassets/Identity.DefaultUI.WebSite/Data/ApplicationDbContext.cs]
public class AppDbContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<Product> Products { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 配置聚合根
modelBuilder.Entity<Order>(b =>
{
b.HasKey(o => o.Id);
b.Property(o => o.Id).HasConversion(
id => id.Value,
value => new OrderId(value));
b.OwnsOne(o => o.ShippingAddress);
// 配置子集合
b.HasMany(o => o.Items)
.WithOne()
.HasForeignKey("OrderId");
});
// 其他实体配置...
}
}
ASP.NET Core DDD项目完整结构
以下是一个符合DDD规范的ASP.NET Core项目结构示例:
src/
├── YourApp.Web/ # 表现层
│ ├── Controllers/ # API控制器
│ │ ├── OrdersController.cs # 订单API
│ │ └── ProductsController.cs # 产品API
│ ├── Models/ # 视图模型
│ └── Program.cs # 应用入口点
│
├── YourApp.Application/ # 应用层
│ ├── Services/ # 应用服务
│ │ ├── OrderService.cs # 订单应用服务
│ │ └── ProductService.cs # 产品应用服务
│ └── DTOs/ # 数据传输对象
│
├── YourApp.Domain/ # 领域层
│ ├── Entities/ # 实体
│ │ ├── Order.cs # 订单实体
│ │ └── Product.cs # 产品实体
│ ├── ValueObjects/ # 值对象
│ │ ├── OrderId.cs # 订单ID
│ │ └── Address.cs # 地址
│ ├── Aggregates/ # 聚合根
│ ├── Repositories/ # 仓储接口
│ └── Services/ # 领域服务
│
└── YourApp.Infrastructure/ # 基础设施层
├── Data/ # 数据访问
│ ├── AppDbContext.cs # EF Core上下文
│ └── Repositories/ # 仓储实现
└── Services/ # 外部服务集成
依赖注入配置
在Program.cs中配置依赖注入,将各层组件连接起来:
// Program.cs 依赖注入配置
var builder = WebApplication.CreateBuilder(args);
// 添加应用服务
builder.Services.AddScoped<IOrderService, OrderService>();
// 添加领域服务
builder.Services.AddScoped<IDiscountService, DiscountService>();
// 添加基础设施
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddScoped<IOrderRepository, EfCoreOrderRepository>();
// 添加控制器
builder.Services.AddControllers();
var app = builder.Build();
// 中间件配置...
app.MapControllers();
app.Run();
实际案例:订单管理系统DDD实现
让我们通过一个简单的订单管理系统,看看DDD在ASP.NET Core中的完整应用。
领域模型实现
首先定义核心领域模型,包括Order聚合根和相关值对象:
// 订单状态枚举
public enum OrderStatus
{
Draft,
Confirmed,
Shipped,
Delivered,
Cancelled
}
// 订单ID值对象
public record OrderId(Guid Value);
// 订单项实体
public class OrderItem
{
public ProductId ProductId { get; private set; }
public int Quantity { get; private set; }
public decimal UnitPrice { get; private set; }
public OrderItem(ProductId productId, int quantity, decimal unitPrice)
{
ProductId = productId;
Quantity = quantity > 0 ? quantity : throw new ArgumentException("数量必须大于0");
UnitPrice = unitPrice >= 0 ? unitPrice : throw new ArgumentException("单价不能为负数");
}
public decimal TotalPrice => Quantity * UnitPrice;
}
// 订单聚合根
public class Order : Entity<OrderId>
{
public CustomerId CustomerId { get; private set; }
public OrderStatus Status { get; private set; }
public Address ShippingAddress { get; private set; }
private readonly List<OrderItem> _items = new();
public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();
public decimal TotalAmount => _items.Sum(item => item.TotalPrice);
public Order(OrderId id, CustomerId customerId, Address shippingAddress)
{
Id = id;
CustomerId = customerId;
ShippingAddress = shippingAddress;
Status = OrderStatus.Draft;
}
public void AddItem(Product product, int quantity)
{
if (Status != OrderStatus.Draft)
throw new InvalidOperationException("只能向草稿订单添加商品");
if (quantity <= 0)
throw new ArgumentException("商品数量必须大于0");
_items.Add(new OrderItem(product.Id, quantity, product.Price));
}
public void Confirm()
{
if (Status != OrderStatus.Draft)
throw new InvalidOperationException("只能确认草稿订单");
if (_items.Count == 0)
throw new InvalidOperationException("订单必须包含至少一个商品");
Status = OrderStatus.Confirmed;
// 可以在这里发布领域事件
AddDomainEvent(new OrderConfirmed(Id));
}
// 其他领域行为...
}
应用服务实现
应用服务协调领域逻辑,处理事务和跨聚合操作:
// 订单应用服务
public class OrderService : IOrderService
{
private readonly IOrderRepository _orderRepository;
private readonly IProductRepository _productRepository;
private readonly IUnitOfWork _unitOfWork;
public OrderService(
IOrderRepository orderRepository,
IProductRepository productRepository,
IUnitOfWork unitOfWork)
{
_orderRepository = orderRepository;
_productRepository = productRepository;
_unitOfWork = unitOfWork;
}
public async Task<OrderDto> CreateOrderAsync(CreateOrderCommand command, CancellationToken cancellationToken)
{
// 验证商品存在且有库存
var products = new List<Product>();
foreach (var item in command.Items)
{
var product = await _productRepository.GetByIdAsync(
new ProductId(item.ProductId), cancellationToken);
if (product == null)
throw new NotFoundException($"商品 {item.ProductId} 不存在");
if (product.Stock < item.Quantity)
throw new InsufficientStockException(product.Id, product.Stock, item.Quantity);
products.Add(product);
}
// 创建订单
var order = new Order(
new OrderId(Guid.NewGuid()),
new CustomerId(command.CustomerId),
new Address(
command.ShippingAddress.Street,
command.ShippingAddress.City,
command.ShippingAddress.State,
command.ShippingAddress.PostalCode,
command.ShippingAddress.Country));
// 添加订单项
for (int i = 0; i < products.Count; i++)
{
order.AddItem(products[i], command.Items[i].Quantity);
}
// 保存订单
await _orderRepository.AddAsync(order, cancellationToken);
await _unitOfWork.SaveChangesAsync(cancellationToken);
// 返回DTO
return new OrderDto
{
Id = order.Id.Value,
Status = order.Status.ToString(),
TotalAmount = order.TotalAmount,
Items = order.Items.Select(item => new OrderItemDto
{
ProductId = item.ProductId.Value,
Quantity = item.Quantity,
UnitPrice = item.UnitPrice
}).ToList()
};
}
// 其他应用服务方法...
}
API控制器实现
最后,在表现层实现API控制器,处理HTTP请求:
// 订单API控制器 [参考 src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs]
[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
private readonly IOrderService _orderService;
public OrdersController(IOrderService orderService)
{
_orderService = orderService;
}
[HttpPost]
public async Task<ActionResult<OrderDto>> CreateOrder(
[FromBody] CreateOrderCommand command,
CancellationToken cancellationToken)
{
var order = await _orderService.CreateOrderAsync(command, cancellationToken);
return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);
}
[HttpGet("{id}")]
public async Task<ActionResult<OrderDto>> GetOrder(
Guid id,
CancellationToken cancellationToken)
{
var order = await _orderService.GetOrderByIdAsync(id, cancellationToken);
if (order == null)
return NotFound();
return order;
}
[HttpPut("{id}/confirm")]
public async Task<IActionResult> ConfirmOrder(
Guid id,
CancellationToken cancellationToken)
{
await _orderService.ConfirmOrderAsync(id, cancellationToken);
return NoContent();
}
// 其他API端点...
}
DDD实践中的常见问题与解决方案
在ASP.NET Core中实施DDD时,开发者常遇到一些挑战,以下是解决方案:
1. 领域事件处理
使用中介者模式(如MediatR)处理领域事件:
// 领域事件
public record OrderConfirmed(OrderId OrderId) : IDomainEvent;
// 事件处理器
public class OrderConfirmedHandler : INotificationHandler<OrderConfirmed>
{
private readonly IEmailService _emailService;
public OrderConfirmedHandler(IEmailService emailService)
{
_emailService = emailService;
}
public async Task Handle(OrderConfirmed notification, CancellationToken cancellationToken)
{
// 发送订单确认邮件
await _emailService.SendOrderConfirmationEmail(notification.OrderId);
}
}
2. 事务管理
使用工作单元模式确保数据一致性:
public interface IUnitOfWork
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
// EF Core工作单元实现
public class EfCoreUnitOfWork : IUnitOfWork
{
private readonly AppDbContext _dbContext;
public EfCoreUnitOfWork(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
return _dbContext.SaveChangesAsync(cancellationToken);
}
}
总结与展望
通过将领域驱动设计与ASP.NET Core结合,我们可以构建出业务逻辑清晰、架构稳定的企业级应用。DDD的分层架构使系统各部分职责明确,依赖注入促进了解耦,而EF Core则为领域模型提供了强大的持久化支持。
随着项目的演进,DDD架构的优势会逐渐显现:
- 业务逻辑集中在领域层,易于理解和维护
- 各层之间的边界清晰,便于团队协作
- 领域模型准确反映业务概念,提高沟通效率
- 系统更具弹性,能够适应业务变化
ASP.NET Core持续发展的特性(如最小API、端点路由等)将进一步简化DDD的实施。未来,随着微服务架构的普及,DDD的限界上下文概念将发挥更大价值,帮助我们构建更灵活的分布式系统。
你是否在ASP.NET Core项目中尝试过DDD?遇到了哪些挑战?欢迎在评论区分享你的经验和问题!关注我们获取更多ASP.NET Core架构实践指南。
Kimi-K2.5Kimi K2.5 是一款开源的原生多模态智能体模型,它在 Kimi-K2-Base 的基础上,通过对约 15 万亿混合视觉和文本 tokens 进行持续预训练构建而成。该模型将视觉与语言理解、高级智能体能力、即时模式与思考模式,以及对话式与智能体范式无缝融合。Python00- QQwen3-Coder-Next2026年2月4日,正式发布的Qwen3-Coder-Next,一款专为编码智能体和本地开发场景设计的开源语言模型。Python00
xw-cli实现国产算力大模型零门槛部署,一键跑通 Qwen、GLM-4.7、Minimax-2.1、DeepSeek-OCR 等模型Go06
PaddleOCR-VL-1.5PaddleOCR-VL-1.5 是 PaddleOCR-VL 的新一代进阶模型,在 OmniDocBench v1.5 上实现了 94.5% 的全新 state-of-the-art 准确率。 为了严格评估模型在真实物理畸变下的鲁棒性——包括扫描伪影、倾斜、扭曲、屏幕拍摄和光照变化——我们提出了 Real5-OmniDocBench 基准测试集。实验结果表明,该增强模型在新构建的基准测试集上达到了 SOTA 性能。此外,我们通过整合印章识别和文本检测识别(text spotting)任务扩展了模型的能力,同时保持 0.9B 的超紧凑 VLM 规模,具备高效率特性。Python00
KuiklyUI基于KMP技术的高性能、全平台开发框架,具备统一代码库、极致易用性和动态灵活性。 Provide a high-performance, full-platform development framework with unified codebase, ultimate ease of use, and dynamic flexibility. 注意:本仓库为Github仓库镜像,PR或Issue请移步至Github发起,感谢支持!Kotlin08
VLOOKVLOOK™ 是优雅好用的 Typora/Markdown 主题包和增强插件。 VLOOK™ is an elegant and practical THEME PACKAGE × ENHANCEMENT PLUGIN for Typora/Markdown.Less00