理解领域驱动设计 (4)

如 Order 和 OrderItem,当我们考虑将其作为一个聚合时,这么使用,是可以的,但是不能说跨聚合也这么用着,如 Enterprise 和 Order,划分时我们更加倾向于划分为两个聚合,遵循保持聚合原则中,引用聚合根的 Id 这一原则,这将改善聚合的边界使其更加清晰,控制更加妥当。

//多聚合间不建议这么用 public class Order : AggregateRoot { //遵循聚合原则引用 Enterprice 聚合根 Id,而不是实例 public int EnterpriceId {get; set;} //public virtual Enterprice Enterprice { get; set; } //... }

考虑到多聚合的协作,便要了解下聚合的首要原则,即在一次事务中,只能更改一个聚合的状态,因此当涉及到多个聚合协作时,如创建订单完毕,需要往库存中某一商品数量减少时,订单本身一般会有商品聚合的标识,借助这个标识,通过领域事件或是集成事件方式,事件接收方将相关联的库存聚合调用起来,以此达到多个聚合间的协作。
又或者考虑到,需要调用商品的信息以使得当前订单中商品信息更加丰富,可通过防腐层调用商品所在上下文远程服务或是应用服务,最终本质上是调用商品聚合中的信息丰富到订单中,也使得多个聚合完成协作。

图片

应用服务

作为限界上下文对外的门户,也即是外观模式的体现。通过用例分析识别出来的用例在此处一一对应存在着,对外提供统一接口,以此满足完整用例场景所需的功能。在应用服务内部,通过编排领域模型对象来完成用例的功能,自身并不包含领域逻辑,但包含着应用逻辑。

图片

可借鉴整洁架构的经典图例来看应用层本身的职责所在,Use Case(用例层)-Application Business Rules,虽然是依靠着领域模型对象才完成的(具体是编排领域模型对象所具有的领域行为),却也说明了应用服务承担着的是用例的职责。

需要注意的是,应用服务的职责不仅限于编排领域模型对象,还需要控制着横切关注点,如验证、日志、事物等的管理。

[UnitOfWork] [Authorize(PermissionNames.PartType_Create)] public async Task CreatePartType(CreatePartTypeDto input) { await _validatingPartTypeManager.CheckUniqueName(input.Name, input.Category);     var partType= PartType.Create(input.Name, input.Description)         .SetCategory(input.Category)         .SetFactory(input.FactoryName);     await _partTypeRepository.InsertAsync(partType);     await _appNotifier.NewPartTypeAsync(); }

如上,事务、认证、请求参数校验(Dto 内),协调领域模型对象和基础设施服务,这是应用服务的职责,当然也不仅限于这些职责。

领域服务

当我们考虑领域逻辑时,首先想到的应该是实体与值对象中具有的领域逻辑,而有些场景下,实体与值对象无法承载这些领域行为,如对多个领域对象作为输入,进行计算并产出一个值对象;又或是需要将操作成集合化的聚合,如在 Supplier 下需要将所有 Order 中的单价汇总,而本身 Supplier 和 Order 是为两个聚合,若考虑借助 Order 去完成该业务操作,不太妥当,在此场景下,可通过领域服务来承载着这些领域行为。

领域服务存在如下特征:

执行一个显著的业务操作过程

对领域对象进行转换

需要使用多个聚合内的实体和值对象编排业务逻辑

领域行为需要访问外部资源

虽说领域服务能够承载领域逻辑,却不能说将所有的领域逻辑都往里塞,如此,导致领域对象贫血。只有当实体与值对象承载不住或是本身并不属于实体或值对象的职责内时,才考虑领域服务来承载,领域服务是一种妥协的结果,并不是说领域服务越多越好。

值对象(Value Object)→ 实体(Entity)→ 领域服务(Domain Service)

如下场景,创建 Invoice,存在几条业务规则,相应 Order 的状态需已完成,并且对应的 Supplier 提供财月信息,这就需要多个聚合的协作,在领域服务编排这些领域对象模型及通过调用外部服务网关,完成业务逻辑。

// InvoiceManager public async Task ValidCheck(string orderId, string supplierId) { var order = await _orderService.GetAsync(orderId); if(!order.IsCompleted()) { throw new UserFriendlyException("Order status is not completed"); } var supplier = await _supplierService.GetAsync(supplierId); if(!supplier.IsCompleted()) { throw new UserFriendlyException("Order status is not completed"); } } public async Task SetFinanceMonth(Invoice invoice, string supplierId) { var supplierFinanceMonth = await _supplierService.GetFinanceMonthAsync(supplierId, Current.Date); if(supplierFinanceMonth == null) { throw new UserFriendlyException("Supplier not provider finance month"); } invoice.SetFinanceMonth(supplierFinanceMonth.StartDate, supplierFinanceMonth.EndDate); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzfwj.html