DDD领域驱动设计实战(六)-领域服务(上)

简介: DDD领域驱动设计实战(六)-领域服务

领域中的服务表示一个无状态的操作,它用于实现特定于某个领域的任务。 当某个操作不适合放在聚合和值对象上时,最好的方式便是使用领域服务。有时我们倾向于使用聚合根上的静态方法来实现这些这些操作,但是在 DDD中,这是一种坏味道。


早期在Product中维护了一个Backlogitem实例的集合。这种建模方式可以计算一个Product的总业务优先级:

image.png

当时这种设计方式非常完美,businessPriorityTotals只需遍历所有 Backlogitem实例,计算出总业务优先级。并且,这种方式适当地使用了值对象 BusinessPriorityTotals。


但我不这么认为,通过对聚合的分析我们知道,这里的Product对象过于庞大,而Backlogitem本身就应该成为一个聚合。因此,上面的实例方法businessPriorityTotals已不再适用于这种场景。


由于Product不再包含Backlogitem集合,团队成员们的第一反应便是使用一个资源库 BacklogltemRepository来获取所需的Backlogitem实例,这是一种好的做法吗?

事实上,团队中的高级开发者并不建议这么做。一个基本原则:应尽量避免在聚合中使用资源库。那么,将businessPriorityTotals()方法声明为静态方法,然后将 Backlogitem集合作为参数传入,如何?

这样,几乎不用对该方法做多少修改,只需传入新参数:

image.png

Product是创建该静态方法的最佳位置吗?

看来要将该方法放在合适的地方并非易事。由于该方法只使用了每个Backlogitem中的值对象,将该方法放在Backlogitem似乎更合适。但这里计算所得的业务价值却属于Product而非Backlogitem,进退维谷!


团队中的高级开发者发话了。他指出:这些问题用一个单一的建模工具即可解决,即领域服务(Domain Service)。


那领域服务是如何工作的?

什么是领域服务

什么不是领域服务?

听到“服务”,自然想到一个远程客户端与某复杂业务系统交互,该场景基本描述了SOA中的一个服务。有多种技术和方法可以实现SOA服务,最终这些服务强调的都是系统层面的

远程过程调用(RPC)或

消息中间件(MQ)

这些技术使得我们可通过服务与分布在不同地方的系统进行业务交互。


以上这些都不是领域服务。

不要将领域服务与应用服务混淆:


应用服务并不会处理业务逻辑

但领域服务恰恰是处理业务逻辑。应用服务是领域模型很自然的客户,也是领域服务的客户。

虽然领域服务中有“服务”这个词,但它并不意味着需要远程的、重量级的事务操作。

何时应该使用领域服务

领域服务到底是什么?当领域中的某个操作或转换过程不是实体或值对象的职责时,此时便应该将该操作放在一个单独的接口,即领域服务。

请确保该领域服务和通用语言是一致的;并且保证它是无状态的。

通常领域模型主要关注特定于某个领域的业务。同样,领域服务也具有相似特点。由于领域服务有可能在单个原子操作中处理多个领域对象,这将增加领域服务的复杂性。


有时,当与另一个限界上下文交互时,领域服务的确需要进行远程操作,但此时重点并非将领域服务作为一个服务提供方,而是将其作为RPC的客户端。


那么何时一个操作不属于实体或值对象?即何时可使用领域服务:


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

对领域对象进行转换

以多个领域对象作为输入进行计算,产生一个值对象结果

计算过程应该具有“显著的业务操作过程”。这也是领域服务很常见的应用场景,它可能需要多个聚合作为输入。

当一个方法不便放在实体或值对象,使用领域服务便是最佳的解决方案。

确定需要领域服务?

请不要过于倾向将一个领域概念建模成领域服务,只有在有必要时才这么做。领域服务不是“银弹”。

过度使用领域服务将导致贫血领域模型,即所有业务逻辑都位于领域服务中,而非实体和值对象


目录
相关文章
|
设计模式 弹性计算 人工智能
阿里技术专家详解DDD系列 第四讲 - 领域层设计规范
在一个DDD架构设计中,领域层的设计合理性会直接影响整个架构的代码结构以及应用层、基础设施层的设计。但是领域层设计又是有挑战的任务,特别是在一个业务逻辑相对复杂应用中,每一个业务规则是应该放在Entity、ValueObject 还是 DomainService是值得用心思考的,既要避免未来的扩展性差,又要确保不会过度设计导致复杂性。
|
SQL 缓存 Java
殷浩详解DDD系列 第三讲 - Repository模式
# 第三讲 - Repository模式 **写在前面** 这篇文章和上一篇隔了比较久,一方面是工作比较忙,另一方面是在讲Repository之前其实应该先讲Entity(实体)、Aggregate Root(聚合根)、Bounded Context(限界上下文)等概念。但在实际写的过程中,发现单纯讲Entity相关的东西会比较抽象,很难落地。所以本文被推倒重来,从Repository
39128 8
|
消息中间件 网络协议 前端开发
殷浩详解DDD:如何避免写流水账代码?
在日常工作中我观察到,面对老系统重构和迁移场景,有大量代码属于流水账代码,通常能看到开发在对外的API接口里直接写业务逻辑代码,或者在一个服务里大量的堆接口,导致业务逻辑实际无法收敛,接口复用性比较差。所以本文主要想系统性的解释一下如何通过DDD的重构,将原有的流水账代码改造为逻辑清晰、职责分明的模块。
殷浩详解DDD:如何避免写流水账代码?
|
微服务 测试技术 Java
阿里技术专家详解 DDD 系列- Domain Primitive
关于DDD的一系列文章,希望能继续在总结前人的基础上发扬光大DDD的思想,但是通过一套我认为合理的代码结构、框架和约束,来降低DDD的实践门槛,提升代码质量、可测试性、安全性、健壮性。
63257 17
阿里技术专家详解 DDD 系列- Domain Primitive
|
设计模式 前端开发 关系型数据库
【DDD】全网最详细2万字讲解DDD,从理论到实战(代码示例) 3
【DDD】全网最详细2万字讲解DDD,从理论到实战(代码示例)
6359 2
|
存储 Java API
阿里高级技术专家谈开源DDD框架:COLA4.1,分离架构和组件(下)
阿里高级技术专家谈开源DDD框架:COLA4.1,分离架构和组件(下)
12147 8
阿里高级技术专家谈开源DDD框架:COLA4.1,分离架构和组件(下)
|
Oracle Java 关系型数据库
Oracle jdk 的国内下载镜像
Oracle jdk 的国内下载镜像
61986 0
就是要你懂负载均衡--lvs和转发模式
> 本文希望阐述清楚LVS的各种转发模式,以及他们的工作流程和优缺点,同时从网络包的流转原理上解释清楚优缺点的来由,并结合阿里云的slb来说明优缺点。 如果对网络包是怎么流转的不太清楚,推荐先看这篇基础:[程序员的网络知识 -- 一个网络包的旅程](https://www.atatech.org/articles/80573) ,对后面理解LVS的各个转发模式非常有帮助。
14277 0
|
前端开发 测试技术 数据库
DDD架构中assembler和converter的区别
在 DDD 四层架构模式中,assembler 和 converter 常用于对象转换,但两者在实际项目中的使用较为随意。本文从英文释义、语义区分和模型层区分三个方面探讨了两者的区别,建议按模型层区分,即 Interface 和 Application 层使用 assembler,Infrastructure 层使用 converter,以避免混淆和随意使用。此外,将转换代码抽离为独立方法有助于保持代码整洁和可测试性。
|
人工智能 前端开发 Java
DDD四层架构和MVC三层架构的个人理解和学习笔记
领域驱动设计(DDD)是一种以业务为核心的设计方法,与传统MVC架构不同,DDD将业务逻辑拆分为应用层和领域层,更关注业务领域而非数据库设计。其四层架构包括:Interface(接口层)、Application(应用层)、Domain(领域层)和Infrastructure(基础层)。各层职责分明,避免跨层调用,确保业务逻辑清晰。代码实现中,通过DTO、Entity、DO等对象的转换,结合ProtoBuf协议,完成请求与响应的处理流程。为提高复用性,实际项目中可增加Common层存放公共依赖。DDD强调从业务出发设计软件,适应复杂业务场景,是微服务架构的重要设计思想。