
Microsoft NLayerApp案例理论与实践 - 领域模型层引言为什么需要领域模型层在传统的分层架构如三层架构中业务逻辑往往被分散在UI层或数据访问层中导致代码耦合度高、可维护性差。Microsoft NLayerApp微软分层应用指南提出了一种以领域模型为核心的分层设计其中领域模型层Domain Model Layer是业务逻辑的核心承载者。它不依赖任何基础设施如数据库、框架而是通过纯C#类表达业务概念和规则。本篇文章将从领域模型的基本概念出发逐步深入到如何在实际项目中设计一个健壮的领域层并配合代码示例帮助理解。—### 一、领域模型层的基本职责领域模型层的主要职责包括-表达业务实体如订单、客户、产品等。-实现业务规则如订单金额必须大于0库存不能为负。-维护业务状态如订单的状态机转换待支付→已支付→已发货。-提供业务操作如计算折扣、校验库存等。一个典型的NLayerApp分层结构如下Presentation Layer (UI) ↓Application Layer (应用服务) ↓Domain Layer (领域模型) ← 本节课重点 ↓Infrastructure Layer (基础设施如EF Core、Repository)领域层是“心脏”其他层都依赖于它而不是反过来。—### 二、从基础开始定义实体和值对象在领域模型中我们通常分为两类对象-实体Entity具有唯一标识ID的对象例如Order。-值对象Value Object没有唯一标识由属性值定义的对象例如Money或Address。#### 代码示例1定义实体和值对象下面是一个简单的订单领域模型包含实体和值对象。csharp// 值对象表示金额public class Money{ public decimal Amount { get; private set; } public string Currency { get; private set; } public Money(decimal amount, string currency) { if (amount 0) throw new ArgumentException(金额不能为负数); Amount amount; Currency currency; } // 值对象之间的相等比较基于属性值 public override bool Equals(object obj) obj is Money other Amount other.Amount Currency other.Currency; public override int GetHashCode() HashCode.Combine(Amount, Currency);}// 实体订单public class Order{ public int Id { get; private set; } // 实体唯一标识 public Money TotalAmount { get; private set; } public DateTime OrderDate { get; private set; } public string Status { get; private set; } public Order(int id, Money totalAmount, DateTime orderDate) { Id id; TotalAmount totalAmount ?? throw new ArgumentNullException(nameof(totalAmount)); OrderDate orderDate; Status 待支付; } // 业务操作支付订单 public void Pay() { if (Status ! 待支付) throw new InvalidOperationException(订单状态不允许支付); Status 已支付; }}注意- 构造函数严格控制状态防止无效数据。- 实体的Id是身份核心值对象没有。- 业务操作如Pay封装了业务规则。—### 三、进阶引入领域服务与仓储接口当业务逻辑涉及多个实体或复杂流程时我们可以使用领域服务Domain Service。同时为了保持领域层的纯粹性我们定义仓储接口Repository Interface但具体实现放在基础设施层。#### 代码示例2领域服务与仓储接口csharp// 领域服务处理订单创建和库存扣减的协作public class OrderService{ private readonly IProductRepository _productRepository; public OrderService(IProductRepository productRepository) { _productRepository productRepository; } public Order CreateOrder(int productId, int quantity, Money price) { var product _productRepository.GetById(productId); if (product null) throw new Exception(产品不存在); if (product.Stock quantity) throw new Exception(库存不足); // 扣除库存 product.DecreaseStock(quantity); _productRepository.Update(product); var total new Money(price.Amount * quantity, price.Currency); return new Order(0, total, DateTime.Now); }}// 仓储接口定义数据访问契约不包含实现public interface IProductRepository{ Product GetById(int id); void Update(Product product);}解释- 领域服务协调多个实体如Product和Order。- 仓储接口让领域层不依赖具体数据库技术遵循依赖倒置原则。- 后续基础设施层如EF Core实现这些接口即可。—### 四、高级实践领域事件与聚合根在复杂业务中我们常使用**聚合根Aggregate Root来保证一致性并通过领域事件Domain Event**解耦副作用。#### 聚合根示例假设一个Customer是聚合根它的订单由Order组成。所有操作必须通过Customer进行以保证数据一致性。csharppublic class Customer{ public int Id { get; private set; } private ListOrder _orders new(); public IReadOnlyCollectionOrder Orders _orders.AsReadOnly(); public void AddOrder(Order order) { if (order.TotalAmount.Amount 10000) throw new Exception(订单金额超过限额); _orders.Add(order); }}领域事件可用于发布“订单已创建”等通知由应用层订阅并执行后续操作如发送邮件。—### 五、领域层设计原则总结1.保持纯粹不引用任何框架或数据库技术。2.业务逻辑内聚所有规则都应在实体或领域服务中实现。3.使用值对象管理不变性如金额、日期范围。4.依赖倒置通过接口让基础设施层依赖领域层。5.聚合根管理一致性避免直接修改子实体。—### 六、完整示例构建一个迷你订单系统为了将上述概念整合我们提供一个完整的迷你示例控制台应用包含实体、值对象、领域服务以及简单的仓储。csharp// 程序入口class Program{ static void Main() { var productRepo new InMemoryProductRepository(); var orderService new OrderService(productRepo); // 创建产品 var product new Product(1, iPhone, 10); // 库存10 productRepo.Add(product); // 下单 var order orderService.CreateOrder(1, 2, new Money(5000, CNY)); Console.WriteLine($订单创建成功总金额{order.TotalAmount.Amount} {order.TotalAmount.Currency}); Console.WriteLine($剩余库存{product.Stock}); }}// 产品实体public class Product{ public int Id { get; private set; } public string Name { get; private set; } public int Stock { get; private set; } public Product(int id, string name, int stock) { Id id; Name name; Stock stock; } public void DecreaseStock(int quantity) { if (quantity 0) throw new Exception(数量不能为负); if (Stock quantity) throw new Exception(库存不足); Stock - quantity; }}// 内存仓储实现仅用于演示public class InMemoryProductRepository : IProductRepository{ private Dictionaryint, Product _products new(); public Product GetById(int id) _products.TryGetValue(id, out var p) ? p : null; public void Add(Product p) _products[p.Id] p; public void Update(Product p) _products[p.Id] p;}运行结果订单创建成功总金额10000 CNY剩余库存8—### 总结领域模型层是NLayerApp架构中表达业务规则和状态的核心。通过本篇文章我们从基础的实体/值对象定义到领域服务、仓储接口再到聚合根和领域事件逐步构建了一个完整的领域层设计视角。关键在于保持领域层的独立性将业务决策集中于此从而让整个系统更易维护和测试。在实践中建议从简单的实体开始逐步引入领域服务和事件避免过度设计。记住领域模型越健壮上层应用就越简单这正是NLayerApp的核心理念之一。希望本文能帮助你更好地理解并应用领域模型层。