开工福利|免费学 2200+ 精品线上课,企业成员人人可得! 了解详情
写点什么

没有依赖注入和面向方面编程,能很好地进行领域驱动设计吗?

  • 2008-03-04
  • 本文字数:2408 字

    阅读完需:约 8 分钟

领域驱动设计(DDD)的主要思想是在设计中将业务领域中的概念与软件元素对应起来,面向对象的编程方法(OOP)是DDD 实现中的核心要素。OOP 中的对象就代表现实中某个实体。在充分利用诸如继承、封装和多态等OOP 概念的基础上, 领域对象会被设计成简单Java 类和接口。

在一个典型的业务单元中,领域对象通常需要与其他对象协同工作,无论此对象是服务(Service) 仓库(Repository) 还是工厂(Factory)。与此同时,诸如领域状态变化跟踪、审核、缓冲、事务管理(包括事务重试)之类的横切关注点也是领域对象必须密切关注的。然而,这些东西大都是可重用的并且是非领域相关的,它们往往会散布或者重复出现在包括领域层的所有代码中。这些嵌入领域代码中的非领域逻辑往往会导致领域层的繁杂与混乱。

为了很好管理代码间依赖,同时解耦领域对象与独立的横切关注点,只使用OOP 技术往往不能为领域驱动设计提供一流的设计和开发解决方案。正是由于这些原因催生了依赖注入 (DI) 和面向方面编程 (AOP) 等概念。它们作为 OOP 的补充,能很好地减少代码间紧密耦合、增加产品模块化特性和管理横切关注点。

以上就是 DomainDrivenDesign 用户组的论坛最近一个帖子讨论的主题。讨论由Ramnivas Laddad 有关DDD 的一段演讲引出,在陈述中他断言DDD 在没有AOP 和DI 辅助的情况下是无法完全实现的。Ramnivas 谈到了应用AOP 实现细粒度DI 这一概念,他认为此概念能使领域对象的行为更合理。他还提到领域对象如果想要有丰富的功能行为,就必须有权使用其他细粒度的对象,这往往是通过将服务、工厂或者仓库注入到领域对象中实现的(使用Aspects 在构造函数或者 setter 函数中进行注入 )。

管理域对象之间的依赖关系(例如实体与其仓库间依赖)是任何一个 DDD 开发者都会涉及的典型问题。通常的设计解决方法是让 Service 或者 Façade 直接调用 Repository,Repository 获得实体对象后将其返回给调用者。这种设计方式往往会导致胖服务层和贫血模型( Anemic Domain Model )的出现,结果就是 façade 类会开始堆积越来越多的业务逻辑,领域对象也逐渐变成只有一些 setter 和 getter 函数的纯粹的数据载体。

InfoQ 与 Ramnivas Laddad 和《领域驱动设计》一书的作者 Eric Evans 就 DDD 实现中 DI 和 AOP 的角色问题进行了讨论。当谈到 DDD 在没有 AOP 和 DI 辅助的情况下是无法完全实现的这一断言时,Ramnivas 认为这个结论的重要前提是领域对象如果想要表现得更灵活,与 Repositories 和 Services 这样的对象打交道是不可避免的。

问题的关键是领域对象怎样得到此类对象?DI 似乎是个正确的选择。AOP 此时也正好派上用场,它将类 Spring 的 DI(将 DI 限制在 application context 中声明的对象范围内)扩展到任意的对象,而不管它是在哪里创建的。当然,AOP 也会在任何领域层面的横切关注点上起作用(此处的基本假设是领域概念应尽可能地映射到某一软件制品上)。

Eric 在谈到 OOP、DI 和 AOP 为何非常适合实现 DDD 时说:

虽然 DDD 能用任何范式实现,只要这些范式支持抽象并且能表达领域概念就行。但在实际应用中人们的选择几乎总是 OOP。因此说 OOP 是 DDD 实践的基础。 DI 目前已经成为了一项广为人知的技术,如果应用得当,它能帮助 DDD 实践者改善设计。在我看来,最重要的还是 DI 实现了领域的隔离。

人们正逐渐认识 AOP 的潜力。这种潜力就是它可以改变领域层的杂乱状态。这对领域概念的清晰表达甚至领域隔离非常有帮助。但是,它还不是“基础”(译者注:指 OOP)的一个经受过实践检验的组成部分,在这一点上它还是跟 OOP 和 DI 有区别的。

以上这些就形成了目前支持 DDD 的最佳实践架构的基础。

他还强调说弄清设计思想(DDD)与能帮助我们实现这种思想的技术工具(OOP、DI 甚或 AOP)间的界限也是非常重要的。

近来定义和管理 Aspects 和 DI 的趋势是应用注释(Annotations)。Spring 采用 @Configurable 将 Repository 和 Services 注入到领域对象。InfoQ 就注释(Annotations)在 DDD 项目中的角色问题咨询了 Eric:

通常,任何依附于某个对象并且代表领域中某个概念的东西,包括注释(Annotation),应该在概念上与此对象有某种联系。@Configurable 属于技术范畴,并非属于领域范畴,因此我不赞成给领域对象加上这样的注释。在另一方面,对 Repository 来说,我一般会有一个接口和一个实现类。我将接口看作其中的领域部分,尽量保持其纯概念化。但是实现类往往会引用某种技术(例如 DAO 是这些技术中的典型代表)。这样在 Repository 的实现类中应用 @Configurable 注释就合情合理了。

在 @Configurable 注释之外,Spring 框架继续扩展了“领域依赖注入”的思想。Ramnivas 最近写了一篇关于 Spring2.5.2 版本( project snapshot build 379 )最新进展的博客文章。Spring2.5.2 有三个新的方面(AnnotationBeanConfigurerAspect, AbstractInterfaceDrivenDependencyInjectionAspect 和 AbstractDepndencyInjectionAspect),它们为领领域对象的依赖注入提供了简单并且灵活的选择。Ramnivas 说引入前述第二个方面(AbstractInterfaceDrivenDependencyInjectionAspect)的目的是让特定领域的注释和接口能起到应有的作用。他最后补充道 @Configurable 虽说是一个简单的选项,但它往往不是最优雅的。

目前也有了一些关于服务编排(对多步骤的业务单元来说)和业务对象生命周期的讨论。在关于 BPEL 是怎样影响领域对象生命周期和不同领域对象之间交互这一问题上,Eric 的观点是:

服务编排和对象生命周期是很微妙的一对概念。服务编排是一种将解耦的业务片段重新结合在一起同时又能描述出工作流程的重要方法。在另一方面,在领域实体有一定的生命周期并且这些周期处于建模的中心地位的情况下,让编制层来管理对象生命周期,领域对象里面就没剩多少东西了。不过此问题并没有现成的标准答案。对设计者设计好坏的评估要个别分析,不能一概而谈。

查看英文原文: Can DDD be Adequately Implemented Without DI and AOP?

2008-03-04 23:143186
用户头像

发布了 23 篇内容, 共 48381 次阅读, 收获喜欢 3 次。

关注

评论

发布
暂无评论
发现更多内容

面向深蓝!西北工业大学团队借助昇腾AI,打造海面落水目标智能机载搜寻系统

Geek_2d6073

数字先锋| 公开!青海师大“接轨社会人才”培养秘籍!

天翼云开发者社区

云计算 大数据 云平台

用javascript分类刷leetcode24.其他类型题(图文视频讲解)

js2030code

JavaScript LeetCode

面试官:能用JavaScript手写一个bind函数吗

helloworld1024fd

JavaScript

从React源码角度看useCallback,useMemo,useContext

flyzz177

React

react源码分析:深度理解React.Context

flyzz177

React

如何实现购物车一键全选?

Towify

从React源码来学hooks是不是更香呢

flyzz177

React

直播|PostgreSQL 技术内幕(五)Greenplum-Interconnect模块

酷克数据HashData

postgresql

2022年后疫情时代旅游业发展的新趋势

易观分析

旅游 后疫情时代

自己手写一个redux

helloworld1024fd

JavaScript

2023前端必会手写面试题整理

helloworld1024fd

JavaScript

王龙:数据烟囱亟需打破 —— 解读云原生数据库的2022

MatrixOrigin

数据库 云原生 MatrixOrigin MatrixOne

react源码分析:babel如何解析jsx

flyzz177

React

技术分享| 如何使用Prometheus实现系统进程监控

anyRTC开发者

Prometheus 服务器 运维开发 数据监控 系统进程监控

2023年工作上的几个小目标

SAP虾客

系统集成 在家办公 PRA 自动化仓库

阿里达摩院5G云网技术融合实践——与世炬网络共同探索智能制造场景应用

云布道师

阿里云 5G

react源码分析:实现react时间分片

flyzz177

React

前端二面vue面试题(边面边更)

bb_xiaxia1998

Vue

字节前端高频手写面试题(持续更新中)

helloworld1024fd

JavaScript

如何实现一个计算器

Towify

带你实现react源码的核心功能

flyzz177

React

超全!9种PCB表面处理工艺大对比

华秋PCB

生产 PCB PCB工艺

坚果的2022年终总结

坚果

年终总结 OpenHarmony

从源代码构建TensorFlow流程记录

京东科技开发者

tensorflow GitHub 开源 源代码 企业号 1 月 PK 榜

了解下 Rust 的所有权机制

ooooooh灰灰

rust 后端 编译

vue组件通信6种方式总结(常问知识点)

bb_xiaxia1998

Vue

TorchDynamo初探:Python ByteCode的动态修改

OneFlow

人工智能 深度学习 TorchDynamo

前端工程师leetcode算法面试必备-二叉树深度广度遍历

js2030code

JavaScript LeetCode

无代码开发

间隔

前端工程师leetcode算法面试必备-二叉树的构造和遍历

js2030code

JavaScript LeetCode

没有依赖注入和面向方面编程,能很好地进行领域驱动设计吗?_架构_Srini Penchikala_InfoQ精选文章