--- title: 领域服务 description: 使用领域服务封装跨聚合的业务逻辑 --- 领域服务(Domain Service)用于封装不自然地属于实体或值对象的领域逻辑。它是无状态的,处理跨多个聚合或实体的业务规则。 ## 什么是领域服务 领域服务具有以下特征: 1. **无状态**:不保存业务状态,只包含业务逻辑 2. **操作多个对象**:通常涉及多个实体或聚合 3. **业务逻辑**:包含重要的领域概念和规则 4. **接口标记**:实现 `IDomainService` 接口 ## 定义领域服务 ```csharp using MiCake.DDD.Domain; // 价格计算服务 public class PricingService : IDomainService { public Money CalculateTotalPrice(Order order, Customer customer) { var subtotal = order.GetSubtotal(); // 应用客户折扣 var discount = customer.GetDiscount(); var discountAmount = subtotal.Multiply(discount.AsDecimal()); // 计算税费 var taxRate = GetTaxRate(order.ShippingAddress); var taxAmount = subtotal.Subtract(discountAmount).Multiply(taxRate); // 计算运费 var shippingCost = CalculateShipping(order, customer); return subtotal .Subtract(discountAmount) .Add(taxAmount) .Add(shippingCost); } private decimal GetTaxRate(Address address) { // 根据地址计算税率 return address.Country == "CN" ? 0.13m : 0.20m; } private Money CalculateShipping(Order order, Customer customer) { if (customer.IsPremium) return Money.Zero("CNY"); return new Money(10.00m, "CNY"); } } // 转账服务 public class TransferService : IDomainService { public void Transfer(BankAccount from, BankAccount to, Money amount) { if (from.Currency != amount.Currency || to.Currency != amount.Currency) throw new DomainException("Currency mismatch"); if (from.Balance < amount) throw new DomainException("Insufficient balance"); from.Withdraw(amount); to.Deposit(amount); } } ``` ## 领域服务 vs 应用服务 ### 领域服务(Domain Service) ```csharp // 领域服务:包含业务规则 public class OrderPricingService : IDomainService { // 计算订单价格的业务规则 public Money CalculateOrderTotal(Order order, List promotions) { var total = order.GetSubtotal(); foreach (var promotion in promotions.Where(p => p.AppliesTo(order))) { total = promotion.Apply(total); } return total; } } ``` ### 应用服务(Application Service) ```csharp // 应用服务:协调领域对象和基础设施 public class OrderApplicationService { private readonly IRepository _orderRepository; private readonly IRepository _customerRepository; private readonly OrderPricingService _pricingService; // 使用领域服务 public async Task CreateOrder(CreateOrderCommand command) { // 1. 加载聚合 var customer = await _customerRepository.FindAsync(command.CustomerId); // 2. 创建订单 var order = Order.Create(customer.Id); foreach (var item in command.Items) { order.AddItem(item.ProductId, item.Quantity, item.Price); } // 3. 使用领域服务计算价格 var total = _pricingService.CalculateOrderTotal(order, customer.GetActivePromotions()); order.SetTotalAmount(total); // 4. 持久化(由环境 UoW 统一提交) await _orderRepository.AddAsync(order); await _unitOfWork.CommitAsync(); // 5. 返回 DTO return MapToDto(order); } } ``` ## 何时使用领域服务 ### ✅ 应该使用领域服务 1. **操作涉及多个聚合**: ```csharp public class OrderFulfillmentService : IDomainService { public bool CanFulfillOrder(Order order, List products) { // 检查多个产品的库存 foreach (var item in order.Items) { var product = products.First(p => p.Id == item.ProductId); if (product.Stock < item.Quantity) return false; } return true; } } ``` 2. **复杂的业务计算**: ```csharp public class ShippingCostCalculator : IDomainService { public Money Calculate(Order order, Address from, Address to) { var distance = CalculateDistance(from, to); var weight = order.GetTotalWeight(); var dimensions = order.GetTotalDimensions(); // 复杂的运费计算逻辑 return CalculateShippingCost(distance, weight, dimensions); } } ``` 3. **业务规则验证**: ```csharp public class CreditCheckService : IDomainService { public bool CheckCredit(Customer customer, Money amount) { var creditLimit = customer.GetCreditLimit(); var outstandingBalance = customer.GetOutstandingBalance(); return outstandingBalance.Add(amount).Amount <= creditLimit.Amount; } } ``` ### ❌ 不应该使用领域服务 1. **属于单个实体的逻辑**: ```csharp // ❌ 不要:这应该是 Order 的方法 public class OrderService : IDomainService { public void AddItemToOrder(Order order, int productId, int quantity) { order.AddItem(productId, quantity); } } // ✅ 正确:直接在 Order 中实现 public class Order : AggregateRoot { public void AddItem(int productId, int quantity) { // 实现逻辑 } } ``` 2. **纯技术性操作**: ```csharp // ❌ 不要:这是基础设施服务,不是领域服务 public class EmailSenderService : IDomainService { public void SendEmail(string to, string subject, string body) { // 发送邮件 } } // ✅ 正确:放在基础设施层 public interface IEmailService { Task SendEmailAsync(string to, string subject, string body); } ``` ## 依赖注入 领域服务通过依赖注入使用: ```csharp // 标记为作用域服务,自动注册 public class PricingService : IDomainService, IScopedService { private readonly ITaxRateProvider _taxRateProvider; public PricingService(ITaxRateProvider taxRateProvider) { _taxRateProvider = taxRateProvider; } public Money CalculateTotal(Order order) { var subtotal = order.GetSubtotal(); var taxRate = _taxRateProvider.GetRate(order.ShippingAddress); return subtotal.Multiply(1 + taxRate); } } // 在应用服务中使用 public class OrderApplicationService { private readonly PricingService _pricingService; public OrderApplicationService(PricingService pricingService) { _pricingService = pricingService; } public async Task ProcessOrder(int orderId) { var order = await _orderRepository.FindAsync(orderId); var total = _pricingService.CalculateTotal(order); order.SetTotal(total); await _unitOfWork.CommitAsync(); } } ``` ## 最佳实践 ### 1. 保持无状态 ```csharp // ✅ 正确 - 无状态 public class DiscountCalculator : IDomainService { public Money Calculate(Order order, Customer customer) { // 不保存任何状态 return order.GetSubtotal().Multiply(customer.DiscountRate); } } // ❌ 错误 - 有状态 public class DiscountCalculator : IDomainService { private Order _currentOrder; // 不要保存状态 public void SetOrder(Order order) { _currentOrder = order; } } ``` ### 2. 使用清晰的方法名 ```csharp public class OrderService : IDomainService { // ✅ 清晰的业务含义 public bool CanBeCancelled(Order order) { } public Money CalculateTotalWithTax(Order order, Address address) { } public bool MeetsMinimumOrderValue(Order order) { } } ``` ### 3. 依赖接口而非实现 ```csharp public class PricingService : IDomainService { private readonly ITaxRateProvider _taxRateProvider; // 接口 private readonly IShippingCalculator _shippingCalculator; // 接口 public PricingService( ITaxRateProvider taxRateProvider, IShippingCalculator shippingCalculator) { _taxRateProvider = taxRateProvider; _shippingCalculator = shippingCalculator; } } ``` ## 实际示例 ### 订单定价服务 ```csharp public class OrderPricingService : IDomainService, IScopedService { private readonly ITaxRateProvider _taxRateProvider; private readonly IShippingCalculator _shippingCalculator; public OrderPricingService( ITaxRateProvider taxRateProvider, IShippingCalculator shippingCalculator) { _taxRateProvider = taxRateProvider; _shippingCalculator = shippingCalculator; } public OrderPricing CalculatePricing(Order order, Customer customer) { // 1. 商品小计 var subtotal = CalculateSubtotal(order); // 2. 应用折扣 var discount = CalculateDiscount(subtotal, customer); // 3. 计算税费 var taxAmount = CalculateTax(subtotal - discount, order.ShippingAddress); // 4. 计算运费 var shippingCost = _shippingCalculator.Calculate( order, customer.ShippingAddress ); return new OrderPricing { Subtotal = subtotal, Discount = discount, Tax = taxAmount, Shipping = shippingCost, Total = subtotal - discount + taxAmount + shippingCost }; } private Money CalculateSubtotal(Order order) { return order.Items .Select(i => i.Price.Multiply(i.Quantity)) .Aggregate((a, b) => a.Add(b)); } private Money CalculateDiscount(Money subtotal, Customer customer) { var discountRate = customer.GetDiscountRate(); return subtotal.Multiply(discountRate.AsDecimal()); } private Money CalculateTax(Money amount, Address address) { var taxRate = _taxRateProvider.GetRate(address); return amount.Multiply(taxRate); } } ``` ## 小结 领域服务用于封装不属于实体或值对象的业务逻辑: - 实现 `IDomainService` 接口 - 保持无状态 - 处理跨多个聚合的操作 - 包含复杂的业务规则 - 通过依赖注入使用 下一步: - 学习[工作单元](/domain-driven/unit-of-work/)理解事务管理 - 阅读[聚合根](/domain-driven/aggregate-root/)理解聚合设计 - 查看[仓储](/domain-driven/repository/)了解数据访问