领域驱动设计之领域模型

埃里克·埃文斯(Eric Evans)已制定了域驱动设计(DDD)。 Martin Fowler是DDD的大力支持者和拥护者。 这些都是非凡的名字,几乎可以肯定的是,他们正在支持一些有价值的东西。 我不是在这里争论。 也许我正在试图证明我编写软件的方式的合理性,或者也许我只是试图清除事物并具有建设性。 让我们来看看。

什么是领域驱动设计的核心- 域模型通用语言的抽象的概念。 我不会对此进行详细介绍–对于那些感兴趣的人,有维基百科(在页脚中有很多参考文献可供阅读)。 从理论上讲,这都是非常好的,并且域驱动的构建软件的方式应该对所有人都有吸引力–毕竟,构建该软件是为了该领域的利益,而不是建筑师,开发人员或QA的利益。

但是现在是实际部分–如何实施DDD? 我将在当代的背景下回答这个问题,即使用spring和hibernate之类的框架。 我会证明它们的用法合理。 Spring是一个非侵入性的依赖注入框架。 Fowler也强烈支持DI,并且DI被认为是实现DDD的好方法。 Hibernate是使用关系数据库时使用对象的一种方法。 另一种方法是使用JDBC并手动构造对象,但这很繁琐。 因此,Hibernate不会影响体系结构部分-它是一种实用程序(当然,功能非常强大)。

在本文中,我将使用“Hibernate”和“弹簧”作为“给定的”,尽管它们可以通过任何DI框架以及任何依赖对象的ORM或其他持久性机制进行更改。

用spring和hibernate实现DDD的公认方法是:

  • 使用@Configurable使域对象有资格进行依赖注入(它们不会在spring之前实例化,因此它们需要这种特殊方法)
  • 在域对象中注入存储库对象,以允许域对象执行与持久性相关的操作
  • 使用薄的,无状态的事务服务层(外观)来协调域对象

这篇广泛的文章中介绍了这种方法的实现和描述。 另一个示例(没有Spring)是http://dddsample.sourceforge.net/ 。 稍后再讨论。

这种方法的替代方法是贫血域模型 。 它被认为是一种反模式,但同时非常普遍并且经常使用。 贫血数据模型的功能很简单–域对象内部没有业务逻辑–它们只是数据持有者。 而是将业务逻辑放在服务中。

之所以将其视为反模式,是因为,首先,这似乎是一种程序方法。 它破坏了封装,因为对象的内部状态完全不是内部状态。 其次,由于领域对象是设计的中心,因此如果它的操作不属于它,而改为多个无状态服务类,则很难更改它。 域驱动的设计针对中型到大型应用程序,这些应用程序发生了很大变化,并且需要一种简便的方法来快速进行更改,而又不会破坏其他功能。 因此,在对象本身内具有对象的所有功能非常重要。 这也可以确保减少重复代码。

因此,代替让服务来计算价格: ProductServiceImpl.calculatePrice(complexProduct),我们应该简单地拥有ComplexProduct.calculatePrice() 。 因此,每当领域专家说价格计算机制发生变化时,更改的地方恰好是一种,也是最直接的一种。

如果考虑简单的操作,这看起来很容易。 但是,当一个域对象需要另一个域对象来完成其工作时,它将变得更加复杂。 使用贫血数据模型,只需将另一个Service注入当前Service并调用其方法即可实现。 使用建议的DDD,可以通过将域对象作为参数来实现。

在我看来,域对象(也是Hibernate实体)已经设置了依赖项。 但不是在Spring之前,因为Spring无法确切知道要注入哪个领域对象。 它们由Hibernate“注入”,因为它确切知道应将哪个(由主键标识)域对象放置在另一个域对象中。 因此,有一个奇怪的例子–如果产品腐烂并且必须在仓库中分配气味,则必须调用例如Warehouse.increaseSmellLevel(getSmellCoeficient()) 。 而且它有精确的仓库,不受弹簧的干扰。

现在,我不同意这一点。 大多数来源(包括上面链接的两个来源)都指出应将存储库/ DAO注入域对象中。 不,他们不应该。 只需调用“保存”或“更新”就不需要了解对象的内部状态。 Hibernate仍然知道一切。 因此,我们只是将整个对象传递到存储库。

让我们将其分为两个部分: 业务逻辑基础架构逻辑 。 域对象不应该了解基础结构。 那可能意味着它不应该知道它被保存在某个地方。 产品是否关心其存储方式? 不,这是“感兴趣”的存储机制。 这里是实际的缺点:

  • CRUD通过简单地将存储库调用包装在所有域对象中来实现–代码重复
  • 域对象可传递地依赖于持久性–即它不是纯域对象,并且如果存储库发生更改,则也必须对其进行更改。 从理论上讲,仅当域规则和属性更改时,才应更改
  • 人们很容易将事务,缓存和其他逻辑包含在域对象中

在上面的一篇文章中,我将在此处为建议的解决方案打开一个括号,以使代码重复和样板代码更易于处理。 建议生成代码。 而且我认为代码生成是一种罪过。 它将无法删除重复的或非常相似的代码并将其抽象化为工具。 最引人注目的示例是生成ProductDAO,CategoryDAO,WarehouseDAO等。生成的代码难以管理,无法扩展且严重依赖于外部元数据,这绝对不是面向对象的方法。

说到存储库,在建议的示例中,每个域对象都应该有一个存储库,该存储库又将调用持久性机制。 那我们得到什么:

用户在UI中按“保存”>保存在服务上的UI调用(以便获得事务支持)>保存在域对象上的服务调用>保存在资源库上的域对象调用>保存在持久性机制上的资源库调用>持久性机制保存对象。

是我自己,还是在这里调用域对象是多余的。 这是一种不增加任何内容的直通方法。 而且由于很多功能与CRUD相关(是的,即使在大型企业应用程序中也是如此),这对我来说似乎很糟糕。

最后,我发现@Configurable方法是一个hack。 它在后台做了一些魔术,这不是任何通用语言功能(也不是设计模式),并且为了了解它是如何发生的,您需要大量的经验。

所以,总结上面的大混乱

  • 域对象不应由Spring(IoC)进行管理,它们不应具有DAO或任何与基础结构相关的内容
  • 域对象具有由Hibernate(或持久性机制)设置的它们依赖的域对象
  • 域对象执行业务逻辑,就像DDD的核心思想一样,但这不包括数据库查询或CRUD –仅对对象内部状态进行的操作
  • 几乎不需要DTO-在大多数情况下,域对象都是DTO本身(这节省了一些样板代码)
  • 服务执行CRUD操作,发送电子邮件,协调域对象,基于多个域对象生成报告,执行查询等。
  • 服务(应用程序)层并不薄,但不包括域对象固有的业务规则
  • 应避免生成代码。 应该使用抽象,设计模式和DI来克服代码生成的需求,并最终–摆脱代码重复。

参考:有关 领域驱动设计,贫乏领域模型,代码生成,依赖项注入等的信息 ,请参见Bozho的技术博客上的JCG合作伙伴 Bozho。

相关文章 :

  • Spring和AspectJ的领域驱动设计
  • 在域驱动设计中使用状态模式
  • ORM问题
  • 什么是依赖倒置? 是IoC吗?
  • 框架使开发人员愚蠢吗?
  • 每个程序员都应该知道的事情
  • JDK中的设计模式
  • Java最佳实践

翻译自: https://www.javacodegeeks.com/2011/09/on-domain-driven-design-anemic-domain.html

领域驱动设计之领域模型

领域驱动设计之领域模型_在领域驱动的设计,贫乏的领域模型,代码生成,依赖项注入等方面……...相关推荐

  1. 在领域驱动的设计,贫乏的领域模型,代码生成,依赖项注入等方面……

    埃里克·埃文斯(Eric Evans)已制定了什么是域驱动设计(DDD). Martin Fowler是DDD的大力支持者和拥护者. 这些都是非凡的名字,几乎可以肯定的是,他们正在支持一些有价值的东西 ...

  2. 领域驱动设计模式设计与实践_在域驱动设计中使用状态模式

    领域驱动设计模式设计与实践 域驱动设计(DDD)是一种开发软件的方法,其中,通过将实现与核心业务概念的不断发展的模型相联系,解决了问题的复杂性. 该术语是由Eric Evans创造的,并且有一个DDD ...

  3. 物流设计大赛优秀作品_第四届广东省大学生物流设计大赛之五邑大学校园选拔赛通知...

    比赛介绍 第四届广东省大学生物流设计大赛 为顺应"大众创业.万众创新"的时代大潮,提高大学 生创新精神.创业意识和创新创业能力,加快高素质物流人 才队伍建设,加强各高校师生间的交流 ...

  4. 电子产品设计emc风险评估_书籍介绍:EMC设计方法与风险评估技术

    <EMC设计方法与风险评估技术>预计于2020年3月由电子工业出版社出版.它是一本全面解读EMC风险评估技术的书籍. 前言与介绍: 本书是基于作者2008年出版的<电子产品EMC设计 ...

  5. 系统怎么设计usb启动_在启动中启动设计系统

    系统怎么设计usb启动 重点 (Top highlight) Design systems are all the rage now and you've probably seen this ter ...

  6. clover更新驱动 后不能开机_黑苹果显卡驱动优化,Metal跑分翻倍!Clover和OC修改方法...

    首先我们要知道什么显卡驱动! 大多数人都是使用的WEG等方法来驱动显卡,用软件测试是否硬解判断显卡是否驱动,但是我有个坏消息要说,这种驱动方式是调用的"兼容性驱动",性能会大打折扣 ...

  7. 公共服务领域英文译写规范_公共领域日:对版权和公共领域重要性的思考

    公共服务领域英文译写规范 The first of the year is Public Domain Day, a day intended to call attention to copyrig ...

  8. scratch设计跑酷游戏_我如何使用Scratch设计游戏

    scratch设计跑酷游戏 作为游戏果酱和开源爱好者的粉丝,我立即被itch.io吸引了去年10月的Open Jam . 我决定使用Scratch编程语言来创建游戏. Scratch面向想要学习编程的 ...

  9. 万年历单片机课程设计百度文库_单片机电子万年历课程设计最终版(样例3)...

    <单片机电子万年历课程设计.doc>由会员分享,可免费在线阅读全文,更多与<单片机电子万年历课程设计(最终版)>相关文档资源请在帮帮文库(www.woc88.com)数亿文档库 ...

最新文章

  1. C语言对mysql数据库的操作
  2. 记忆网络RNN、LSTM与GRU
  3. 解决Ubuntu的错误提示
  4. dart参数传方法_为 JavaScript 开发人员准备的 Dart 参考教程
  5. HDU - 1875 畅通工程再续
  6. Scala 闭包详解
  7. 服务器虚拟化 需求,虚拟化的优势与需求分析
  8. Linux系统编程 --- 系统调用
  9. JDBC连接池DBUtils使用
  10. 安卓和ios的ui设计区别_【交互设计】 也许这些才是你作品集最需要的
  11. 电商后台设计-组织架构(三)
  12. 计算机固态硬盘序列号,固态硬盘检测工具ssdlife pro 2安装教程(附序列号)
  13. 搭建服务器中转视频加速,高清视频站服务器的秘密诀窍:G口独享带宽不限流量+高速SSD...
  14. android外设按键,Android 外接键盘的按键处理
  15. 【网络】路由器集成锐捷认证
  16. python alpha通道_Python PIL完全删除每个alpha通道
  17. 软件工程考研笔记整理(三小时速成)(1)
  18. Youtube 开发通过遥控机控制实现快进快退功能
  19. 计算机主板是cpu吗,主板和cpu是一起的吗
  20. Python语法错误和异常

热门文章

  1. 使用ueditor实现多图片上传案例——Dao层(IBaseDaoUtil)
  2. Redis(案例四:购物车实现案例-Hash数据)
  3. 电脑基本快捷键的使用
  4. java安装显示更改_java 安装与配置
  5. Navicat Premium 11 12 闪退
  6. ArrayList整理
  7. android查看wifi是否双频,Android判断wifi是5G还是2.4G
  8. 端到端的地址翻译(虚拟地址是怎样取到相应高速缓存的数据的?)
  9. java堆内与堆外数据交互_Java:汇总堆外数据
  10. java orm框架有哪些_Java Stream ORM现在带有JOIN