聚合根的实体类中没有业务逻辑代码,只有对象的创建、对象的初始化、状态管理等与个体相关的代码。
对于聚合内的业务逻辑,我们编写领域服务(domain service),而对于跨聚合协作的逻辑,我们编写应用服务(application service)。
应用服务协调多个领域服务来完成一个用例。
在DDD中,一个典型的用例的处理流程如下:
第1步,准备业务操作所需要的数据,一般是从数据库或者其他外部数据源获取数据。
第2步,执行由一个或者多个领域模型做出的业务操作,这些操作会修改实体类的状态,或者生成一些操作结果。
第3步,把对实体类的改变或者操作结果应用于外部系统,比如保存对实体类的修改到数据库或者把计算结果通过短息发送给用户。
在上面的步骤中,只有第1步和第3步涉及与外部系统的交互,第2步是对领域模型的业务逻辑操作。
这3步组成一个典型的应用服务的逻辑,而若干个领域服务被编排完成第2步的工作。
领域模型与外部系统(数据库、缓存等)是隔离的,不会发生直接交互,也就是说领域服务不会涉及读写数据库的操作。
这样我们可以很好地实现责任的划分:
业务逻辑放入领域服务,而与外部系统的交互由应用服务来负责。
因为领域服务是用来协调领域对象完成业务逻辑操作的,所以领域服务是无状态的,状态由领域对象来管理。
需要注意的是,领域服务不是必须的,在一些简单的业务处理(比如增、删、改、查)中是没有领域知识(也就是业务逻辑)的,这种情况下应用服务可以完成所有操作,不需要引入领域服务,这样我们可以避免系统出现过度设计的问题。
如果随着系统的进化,应用服务中出现了业务逻辑,我们就要把业务逻辑放入领域服务。
和聚合相关的两个概念是”仓储”(repository)和”工作单元”(unit of work)。
聚合中的实体类不负责数据的读取和保存,这些工作是由仓储负责的,因此实体类负责业务逻辑的处理,仓储负责按照要求从数据库中读取数据以及把领域服务修改的数据保存回数据库,一个聚合对应一个用来实现数据持久化的仓储。
聚合内数据操作的关系是非常紧密的,我们要保证事务的强一致性,而聚合间的协作是关系不紧密的,因此我们只要保证事务的最终一致性即可。
聚合内的若干相关联的操作组成一个”工作单元”,这些工作单元要么全部成功,要么全部失败。
因此领域服务不依赖外部系统、不保存状态,所以领域服务比应用服务更容易进行单元测试,这对于提供系统的质量是非常有帮助的。