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

领域驱动设计之领域模型

埃里克·埃文斯(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

领域驱动设计之领域模型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/359793.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

struts2与struts1整合,java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

原因:我往项目的WEB-INF/lib中导入了struts2基本的包,还有struts1的core包,以及struts2-strut1-plugin的包,但是没有导入commons-loggin-1.3.1这个包,如下图 我打开commons-loggin-1.3.1这个包看了一下,果然…

debian 查看php版本,Debian 下轻松实现 PHP 多版本共存

PHP7 的发布废弃了部分特性,一些程序或者插件当前不能很好的运行在 PHP7 上,因此如果服务器上能同时运行多个 PHP 的版本,就可以很好的解决过渡期的兼容性问题。在前面两篇文章中,我们介绍了如何在 Debian Stretch 中安装使用 PHP…

query和prototype库冲突的解决方法

我们在使用 jQuery 的过程中我们会频繁使用 $ 符号,$是JQuery的别名,所有使用$的地方也都可以使用JQuery来替换,例如 如$(#msg)等同于JQuery(#msg)的写法。 在页面中同时引入另一个js类库,并且该类库也是用了 $ 符号时&#xff0c…

php启用openssl,php开启openssl的方法

php开启openssl的方法,大多数情况下openssl是没有开启的,要想启用需要进行下简单的设置windows下开启方法:1: 首先检查php.ini中;extensionphp_openssl.dll是否存在, 如果存在的话去掉前面的注释符‘&#…

apache lucene_全文搜索Apache Lucene简介

apache lucene在本教程中,我想谈谈Apache Lucene 。 Lucene是一个开源项目,提供基于Java的索引和搜索技术。 使用其API,很容易实现全文搜索 。 我将处理Lucene Java版本 ,但请记住,还有一个名为Lucene.NET的.NET端口&a…

php 运算验证码类,php 数学运算验证码实现代码

//-------------------------------------// 文件说明:数学运算验证码// 文件作者:Jesse Lee// 最后更新:2008-09-07//-------------------------------------session_start();$sessionvar vdcode; //Session变量名称$width 150; //图像宽度…

经典ICP算法的问题

最近可能要用三维点云实现一个三维场景重建的功能,从经典的ICP算法开始,啃了一些文档,对其原理也是一知半解。 迭代最近点算法综述 大致参考了这份文档之后,照流程用MATLAB实现了一个简单的ICP算法,首先是发现这份文档…

php 405跳转,php – 返回HTTP 405的CORS预检请求

我正在尝试创建一个RESTful Web服务,并且已经停止实现PUT请求.我尝试过但未能在本网站上关注其他答案以及Mozilla的各种文章.该请求是从域wwwtest.dev-box生成的,它将转到test.dev-box(基本上是一个调用后端应用程序的前端应用程序).以下是我从Live HTTP标头中捕获的标头&#…

wowza rtsp_使用wowza和xuggler将RTMP转为RTSP

wowza rtsp注意:这是我们的“ Xuggler开发教程 ”系列的一部分。 大家好! 在过去的三个月中,我们一直在进行电话会议项目。 我们认为,使用诸如Flex之类的技术的基于Web的应用程序将是此类要求苛刻的项目的最佳方法。 随着软件的复…

iOS执行时工具-cycript

cycript是大神saurik开发的一个很强大的工具,能够让开发人员在命令行下和应用交互,在执行时查看和改动应用。它确实能够帮助你破解一些应用,但我认为这个工具主要还是用来学习其它应用的设计(主要是UI的设计及实现)。 …

开启php soap,php soap 开发文档

一. 必备知识1.wsdl(web服务标记语言)WSDL(网络服务描述语言,Web Services Description Language)是一门基于 XML 的语言,用于描述 Web Services 以及如何对它们进行访问。具体参考请访问下面网址2.soapSOAP 是一种简单的基于 XML 的协议&…

知识点2

1. DUMMY是不是检查所有的类型的权限呢?PS:不是,dummy的意思是虚拟的意思,就是说权限检查的时候有这个权限检查字段,但是不对该字段做权限检查。AUTHORITY-CHECK OBJECT Z_BRANDID ACTVT DUMMY ID BRAND FIELD p_br…

java调用jndi出错,无法使用Java JNDI上下文查找来访问对象

我正在运行Tomcat6并希望从我的Servlet访问数据源。但我得到了javax.naming.OperationNotSupportedException: cant generate an absolute name for this namespaceat org.apache.naming.NamingContext.getNameInNamespace(NamingContext.java:772)我的context.xml在HomeContro…

java是如何实现原语的_Java中的低GC:使用原语而不是包装器

java是如何实现原语的总览 有两个很好的理由在可能的地方使用原语而不是包装器。 明晰。 通过使用原语,您可以清楚地知道null值是不合适的。 性能。 使用原语通常更快。 清晰度通常比性能更重要,并且是使用它们的最佳理由。 但是,本文讨论…

MFC 错误异常,用vs添加资源并为资源定义类后报错:error C2065 : 未声明的标识符...

添加了一个Dialog资源,修改了ID之后右击资源添加了一个类,在类里面有一个成员变量: // 对话框数据 enum { IDD IDD_GETIN }; 而在编译过程中出现报错,错误代号是error C2065 : 未声明的标识符,我的第一反应是为什么…

php 正则匹配数字范围,正则表达式之匹配数字范围

最近有个需求就是根据产品编号批量下架产品,需要下架日期为16-31号之间的产品,比如编号为B201607280023匹配表达式如下:^201607(1[6-9]|2[0-9]|3[0-1]).逻辑很简单,如果是必须是1或2或3开头,如果是1开头则后面范围为6-9&#xff0…

php 取utc时间,得到UTC时间在PHP

使用gmdate将始终返回GMTdate。 语法与date相同。一个简单的gmdate()就足够了$time time(); $check $timedate("Z",$time); echo strftime("%B %d, %Y %H:%M:%S UTC", $check);正如以前在这里回答的那样 ,从PHP 5.2.0开始,您可以…

BrnShop开源网上商城第二讲:ASP.NET MVC框架

BrnShop开源网上商城第二讲:ASP.NET MVC框架 原文:BrnShop开源网上商城第二讲:ASP.NET MVC框架在团队设计BrnShop的web项目之初,我们碰到了两个问题,第一个是数据的复用和传递,第二个是大mvc框架和小mvc框架的选择。下…

tomcat不停机部署_Tomcat中的零停机部署(和回滚); 演练和清单

tomcat不停机部署亲爱的大家, 如果您认为Tomcat不能再进步,那您就错了。 Tomcat 7引入了所谓的并行部署 。 这是由SpringSource / VMWare贡献的。 简而言之,并行部署是指能够并行部署一个以上版本的Web应用程序,从而使所有版本都…

dataframe建一个空的,创建一个空的Pandas DataFrame,然后填充它?

我想用时间序列计算中的值迭代地填充数据框 . 所以基本上,我想初始化数据框,包括列A,B和时间戳行,全部为0或全部为NaN .然后我会添加初始值并检查此数据,计算前一行中的新行,比如行[A] [t] 行[A] [t-1] 1左…