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是值得用心思考的,既要避免未来的扩展性差,又要确保不会过度设计导致复杂性。
|
消息中间件 网络协议 前端开发
殷浩详解DDD:如何避免写流水账代码?
在日常工作中我观察到,面对老系统重构和迁移场景,有大量代码属于流水账代码,通常能看到开发在对外的API接口里直接写业务逻辑代码,或者在一个服务里大量的堆接口,导致业务逻辑实际无法收敛,接口复用性比较差。所以本文主要想系统性的解释一下如何通过DDD的重构,将原有的流水账代码改造为逻辑清晰、职责分明的模块。
殷浩详解DDD:如何避免写流水账代码?
|
微服务 测试技术 Java
阿里技术专家详解 DDD 系列- Domain Primitive
关于DDD的一系列文章,希望能继续在总结前人的基础上发扬光大DDD的思想,但是通过一套我认为合理的代码结构、框架和约束,来降低DDD的实践门槛,提升代码质量、可测试性、安全性、健壮性。
63023 17
阿里技术专家详解 DDD 系列- Domain Primitive
|
SQL 缓存 Java
殷浩详解DDD系列 第三讲 - Repository模式
# 第三讲 - Repository模式 **写在前面** 这篇文章和上一篇隔了比较久,一方面是工作比较忙,另一方面是在讲Repository之前其实应该先讲Entity(实体)、Aggregate Root(聚合根)、Bounded Context(限界上下文)等概念。但在实际写的过程中,发现单纯讲Entity相关的东西会比较抽象,很难落地。所以本文被推倒重来,从Repository
38953 8
|
存储 Java API
阿里高级技术专家谈开源DDD框架:COLA4.1,分离架构和组件(下)
阿里高级技术专家谈开源DDD框架:COLA4.1,分离架构和组件(下)
11978 8
阿里高级技术专家谈开源DDD框架:COLA4.1,分离架构和组件(下)
|
缓存 前端开发 中间件
DDD 领域驱动设计落地实践系列:工程结构分层设计
前面几篇文章中,笔者给大家阐述了 DDD 领域驱动设计的三大过程,重点围绕如何通过战略设计与战术设计进行 DDD 落地实践进行了详细的讨论,但是还没有涉及到工程层面的落地。实际上所有的这些架构理论到最后都是为了使得我们代码结构更加清晰,从而开发出 bug 少、扩展性强、逻辑清楚的应用。因此本文就是为了解决 DDD 领域驱动落地实践最后一公里问题,将我们分析出来的领域模型通过与工程结构的映射实现真正的落地。
DDD 领域驱动设计落地实践系列:工程结构分层设计
|
设计模式 前端开发 关系型数据库
【DDD】全网最详细2万字讲解DDD,从理论到实战(代码示例) 3
【DDD】全网最详细2万字讲解DDD,从理论到实战(代码示例)
6212 2
|
前端开发 测试技术 数据库
DDD架构中assembler和converter的区别
在 DDD 四层架构模式中,assembler 和 converter 常用于对象转换,但两者在实际项目中的使用较为随意。本文从英文释义、语义区分和模型层区分三个方面探讨了两者的区别,建议按模型层区分,即 Interface 和 Application 层使用 assembler,Infrastructure 层使用 converter,以避免混淆和随意使用。此外,将转换代码抽离为独立方法有助于保持代码整洁和可测试性。
|
消息中间件 测试技术 领域建模
DDD - 一文读懂DDD领域驱动设计
DDD - 一文读懂DDD领域驱动设计
50594 6
|
消息中间件 运维 数据库
架构设计之解析CQRS架构模式!
架构设计之解析CQRS架构模式!
734 1
架构设计之解析CQRS架构模式!

热门文章

最新文章