ABP框架领域层
领域层
•实体
•仓储
•领域服务
•工作单元(下期)
•领域事件(事件总线)(下期)
•实体是DDD(领域驱动设计)的核心概念之一。
•Eirc Evans是这样描述的实体的:“它根本上不是通过属性定义的,而是通过一系列连续性(continuity)和标识(identity)定义的”。
•因此,实体都有Id属性并且都存储到数据库中。
•一个实体一般会映射到数据库的一张表。
在ABP中,实体派生自Entity类,Entity默认是int(int32)
对仓储的定义:
•位于领域层和数据映射层之间,使用类似集合的接口来访问领域对象。
•在实践中,仓储是执行领域对象(实体和值对象)的数据库操作。一般地,一个分离的仓储用于一个实体(或者聚合根)。
IRepository接口
在ABP中,一个仓储类应该实现一个IRepository接口。为每一个仓储定义一个接口是一个好的做法。
IRepository接口:查询
获得单个实体
IRepository接口:查询
获得多个实体
IRepository接口:插入
获得多个实体
注:Insert、InsertOrUpdate 方法不会即时执行到数据库。只有带 AndGetId 的方法才会即时执行到数据库。
当工作单元完成时才统一执行到数据库。
IRepository接口:更新、删除
注:更新、删除方法不会即时执行到数据库。
当工作单元完成时才统一执行到数据库。
IRepository接口:使用
IRepository接口:最佳实践
•仓储类应该是无状态的。这意味着,你不应该定义仓储级别的状态对象,而且一个仓储方法调用不应该影响其他的调用。
•自定义仓储方法不应该包含业务逻辑或者应用逻辑,而应该只执行数据相关的或者orm特定的任务。
•当仓储使用依赖注入时,给其他服务定义更少的或者不要定义依赖。
领域服务(或DDD中的服务)用于执行领域操作和业务规则。Eric Evans描述了一个好的服务应该具备下面三个特征:
1.和领域概念相关的操作不是一个实体或者值对象的本质部分。
2.接口定义在领域模型其他元素的条款中。
3.操作是无状态的。
跟获得或返回一个数据传输对象的应用服务方法(DTO)不同,领域服务获得或者返回一个领域对象(比如实体或值类型)。
一个领域服务可以用于应用服务,也可以用于其他的领域服务,但不能直接用于展现层,服务层才直接用于展现层。
IDomainService接口和DomainService类
1.ABP定义了IDomainService接口,所有的领域服务都按照惯例实现了该接口。当实现时,领域服务会以transient自动注册到依赖注入系统。
●
2.此外,领域服务(可选地)可以从DomainService类继承。因此,它可以使用一些继承的属性,比如logging,本地化等等。当然,如果没有继承,如果需要的话也可以注入这些属性。
讨论:
为什么不使用应用服务实现领域服务中的逻辑呢?
我们可以简单地说,它不是应用服务要干的活。因为领域逻辑不是一个用例(use-case),而是一个 业务操作。我们可以在不同的用例中使用相同的“将一个任务派给一个人”的逻辑。比如说我们以后会更新这个任务,并且将这个任务派给其他人。
因此,我们可以使用相同的领域逻辑,这个逻辑就是“将一个任务派给一个人”,我们不用考虑这个具体的人和具体的任务。此外,我们可能有两个不同的UI(一个移动端应用和一个web应用)来共享相同的领域。
如何强制使用领域服务:
开发这个应用服务的开发者可能不知道存在一个TaskDomainService,而且可以直接将给定的 PersonId设置给任务的 AssignedPersonId。那么,如何阻止他这样做呢?
回顾
•实体:定义实体常用的几种基类 Entity、CreationAuditedEntity、AuditedEntity、FullAuditedEntity、IPassivable、AggregateRoot
•仓储:仓储提供数据层常用的操作方法,仓储的定义与使用方式,仓储使用的最佳实践和建议。
•领域服务:领域服务存在的意义、职责,如何定义一个领域服务。