架构层和建模域逻辑

在讨论用于建模域逻辑(例如事务脚本,表模块,域模型)的PoEAA模式时,我注意到人们对域模型模式是最好的印象(尽管印象不对)。 因此,他们开始将其应用于所有内容。

不配领域模型模式

让我们变得真实。 大多数子系统都是基于CRUD的。 系统的仅特定部分需要域模型实现模式。 或者,换句话说,应用程序的某些部分只需要数据上的表格和一些验证逻辑(例如,必填/必填字段,数字的最小/最大值,文本的最小/最大长度)。 对于这些,领域模型是不值得的。

对于这些,也许贫血领域模型会很好地适合。

贫血领域模型并不像听起来那样糟糕

贫血领域模型并不像听起来那样糟糕。 在那里,我说了(至少在我的博客文章中如此)。

但是看起来怎么样?

类图1

package com.acme.bc.domain.model;
...
@Entity
class Person {@Id ... private Long id;private String firstName;private String lastName;// ...// getters and setters
}
...
interface PersonRepository /* extends CrudRepository<Person, Long> */ {// CRUD methods (e.g. find, find/pagination, update, delete)
}
package com.acme.bc.infrastructure.persistence;
...
class PersonRepositoryJpa implements PersonRepository {...
}

在表示层中,控制器可以访问存储库。 该存储库负责提取持久性详细信息。

package com.acme.bc.interfaces.web;@Controller
class PersonsController {private PersonRepository personRepository;public PersonsController(PersonRepository personRepository) {...}// ...
}

在这种情况下,将Person类暴露给表示层是完全可以的。 表示层可以直接使用它,因为它具有一个公共的零参数构造函数,获取器和设置器,而视图很可能需要这些构造器。

那里有它。 一个简单的基于CRUD的应用程序。

您还需要服务层吗? 否。您还需要DTO (数据传输对象)吗? 不需要。在这种简单的CRUD情况下,您不需要其他服务或DTO

是的,此Person看起来像域实体。 不过,这并不包含逻辑,并且只是用来传输数据。 因此,它实际上只是一个DTO。 但这没关系,因为它可以完成保存持久性存储和检索的数据的工作。

现在, 如果业务逻辑开始变得更加复杂,则最初贫乏的领域模型中的某些实体可能会变得更加富于行为。 如果是这样的话,这些实体可以值得一个领域模型模式。

贫血领域模型的替代品

作为贫血领域模型的替代方案(如上所述),可以将这些类移出领域逻辑层并移入表示层。 而不是命名
PersonRepository ,现在命名为
PersonDao

类图0

package com.acme.bc.interfaces.web;@Entity
class Person {...}@Controller
class PersonsController {private PersonDao personDao;public PersonsController(PersonDao personDao) {...}// ...
}interface PersonDao /* extends CrudRepository<Person, Long> */ {// CRUD methods (e.g. find, find/pagination, update, delete)
}
package com.acme.bc.infrastructure.persistence;class PersonDaoJpa implements PersonDao {...
}

太多分层

我认为,如果您必须通过不增加价值的强制性应用程序服务,那将是一个过大的杀伤力。

package com.acme.bc.interfaces.web;
...
@Controller
class PersonsController {private PersonService personService;public PersonsController(PersonService personService) {...}// ...
}
package com.acme.bc.application;
...
@Service
class PersonService {private PersonRepository personRepository;public PersonService(PersonRepository personRepository) {...}// expose repository CRUD methods and pass to repository// no value add
}

将存储库保留在domain.model包中。 将存储库实现放置在另一个包中(例如, infrastructure.persistence )。 但为什么?

domain.model包是定义存储库的位置。 域模型中的元素指示存储库接口定义中需要哪些方法。 因此,存储库定义放置在domain.model包中。 存储库实现需要遵循添加的新方法(或删除未使用的方法)。 此包装遵循依赖关系反转原理。 infrastructure.persistence程序包取决于domain.model程序包,而不是相反。

交易申请服务

那么,什么时候应用服务合适? 应用程序服务负责驱动工作流程和协调事务管理(例如,通过使用Spring中的声明性事务管理支持)。

如果您发现简单的CRUD应用程序需要在表示层控制器中启动事务,那么将它们移入应用程序服务可能是一个好兆头。 当控制器需要更新多个不具有单个根的实体时,通常会发生这种情况。 这里通常的示例是在银行帐户之间转移金额。 需要进行交易以确保借方和贷​​方都成功或都失败。

类图2

package sample.domain.model;
...
@Entity
class Account {...}
...
interface AccountRepository {...}
package sample.interfaces.web;
...
@Controller
class AccountsController {private AccountRepository accountRepository;...@Transactionalpublic ... transfer(...) {...}
}

如果看到了这一点,那么最好将其(从表示层)移至应用程序层服务。

package sample.interfaces.web;
...
@Controller
class AccountsController {private AccountRepository accountRepository;private TransferService transferService;...public ... transfer(...) {...}
}
package sample.application;
...
@Service
@Transactional
class TransferService {private AccountRepository accountRepository;...public ... transfer(...) {...}
}
package sample.domain.model;
...
@Entity
class Account {...}
...
interface AccountRepository {...}

复杂逻辑的域模型模式(仅)

我将以重复输入记帐为例。 但我敢肯定,还有更适合的复杂逻辑。

假设我们将日记帐分录和帐户建模为域实体。 该帐户包含余额(金额)。 但这不是一个简单的数字。 需要创建日记帐分录。 过帐日记帐分录时,它将影响指定的帐户。 然后,该帐户将更新其余额。

package ….accounting.domain.model;
...
/** Immutable */
@Entity
class JournalEntry {// zero-sum items@ElementCollectionprivate Collection<JournalEntryItem> items;...
}
...
/** A value object */
@Embeddable
class JournalEntryItem {...}
...
interface JournalEntryRepository {...}
...
@Entity
class Account {...}
...
interface AccountRepository {...}
...
@Entity
class AccountTransaction {...}
...
interface AccountTransactionRepository {...}

现在,在这种情况下,一个简单的实现将有一个表示层控制器创建一个日记帐分录对象,并使用一个存储库来保存它。 并且在某个时间点(或如果使用自动过帐),将创建相应的帐户交易,并更新帐户余额。 所有这些都需要汇总成一个事务(即全有或全无)。

再次,此事务理想地移至应用程序服务。

package ….accounting.application;@Service
@Transactional
class PostingService {...}

如果需要允许用户浏览日记帐分录和帐户交易,则表示层控制器可以直接使用相应的存储库。 如果域实体不适合于视图技术(例如,它不遵循JavaBean命名约定),则表示层可以定义适合于视图的DTO。 小心! 不要仅仅为了满足表示层的需要而更改域实体。

package ….interfaces.web;@Controller
class AccountsController {private AccountRepository accountRepository;private AccountTransactionRepository accountTransactionRepository;private PostingService postingService;...
}

即将结束...

所以你有它。 希望这篇文章可以阐明何时(以及何时不)使用域模型模式。

类图3

现在我想我要感冒了。

奶油啤酒

翻译自: https://www.javacodegeeks.com/2016/10/architectural-layers-modeling-domain-logic.html

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

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

相关文章

一步步通过命令行cl.exe编译Windows程序

Windows系统下有强大的IDE工具VS&#xff0c;VC6.0等&#xff0c;隐藏了很多编译和链接的细节。事实上&#xff0c;VS是通过调用cl.exe进行源代码编译。接着调用link.exe进行目标文件的链接等操作。有时候&#xff0c;如果出现编译或者链接错误&#xff0c;我们也可以通过命令行…

将数据流链接到加密转换的流CryptoStream 类

1.命名空间:System.Security.Cryptography 程序集:mscorlib&#xff08;在 mscorlib.dll 中&#xff09;----------------------------------------------------继承层次结构-System.Object----- System.MarshalByRefObject--------- System.IO.Stream-------------System.Secu…

Spring Batch:多种格式输出编写器

作为Spring Batch的坚定倡导者&#xff0c;我一直在谈论Spring Batch的概念&#xff0c;它为开发人员提供了一个框架&#xff0c;使他们可以专注于解决业务需求。 这样&#xff0c;它使开发人员不必花费过多的时间来解决所有技术问题以支持该解决方案。 为了说明我的意思&…

《七哥说道》第五章:入职惨做苦力,画饼一望无际

【转载请注明】&#xff1a; 原文出处&#xff1a;https://www.cnblogs.com/jstarseven/p/10521426.html 作者&#xff1a;jstarseven 码字挺辛苦的..... “你是住在回龙观东大街附近吗&#xff0c;那先不急下班&#xff0c;一会我开车顺带你回去吧&#xff01;” 第二家公司的…

通过源码将git升级到最新版

因为go-get下载hugo失败&#xff0c;需要升级git到最新版本。 下载最新git代码 wget -O git-master.zip https://github.com/git/git/archive/master.zip编译 unzip git-master.zip cd git-masteryum install openssl-devel curl-devel expat-devel perl-ExtUtils-MakeMaker …

win10只有edge浏览器能上网的解决方法

问题描述&#xff1a;电脑就只有edge浏览器和自带的邮件可以上网&#xff0c;但是QQ Chrome&#xff0c;360浏览器都无法上网&#xff1b; 解决方法&#xff1a;打开命令提示符&#xff08;管理员&#xff09;&#xff0c;执行netsh winsock reset&#xff0c;重启系统就好了。…

CTF dotNet逆向分析

题目来源http://ctf.idf.cn/index.php?ggame&marticle&aindex&id36 .NET逆向第一题 嗯&#xff0c;看名字就应该明白了&#xff0c;快去下载吧&#xff01; http://pan.baidu.com/s/1bnvVbp9 下载后是一个DotNetCrackMe1.exe文件。 分析 逆向分析的基础问题&…

xaas_从XaaS到Java EE – 2012年哪款该死的云最适合我?

xaas您是否曾经想过要使Java EE正常运行需要什么&#xff1f; 是的 多年。 从托管我自己的主机开始&#xff0c;转到一些托管产品 &#xff0c;最后偶然发现了PaaS运动。 老实说&#xff0c;我并没有太认真。 我只是想把我的东西放到某个地方&#xff0c;而不在乎解决方案。 在…

day3----python变量与常量

一、python变量&#xff1a; 1、概述&#xff1a;&#xff08;1&#xff09;程序可操作的存储空间的名称&#xff1b;&#xff08;2&#xff09;程序运行期间可以改变的数据&#xff08;3&#xff09;每个变量都有特定的类型 2、作用&#xff1a;将不同类型的数据存储到内存中 …

垃圾收集中的代际差异

去年&#xff0c;我一直在帮助新兴公司Instana创建一个Java代理&#xff0c;该代理可跟踪Java应用程序中的执行情况。 收集并结合此执行数据以生成用户请求以及系统所有者半球内服务之间的最终通信的跟踪。 这样&#xff0c;可以可视化非结构化通信&#xff0c;从而显着简化了由…

简单解释 MapReduce 算法

一个有趣的例子 你想数出一摞牌中有多少张黑桃。直观方式是一张一张检查并且数出有多少张是黑桃&#xff1f; MapReduce方法则是&#xff1a; 给在座的所有玩家中分配这摞牌 让每个玩家数自己手中的牌有几张是黑桃&#xff0c;然后把这个数目汇报给你 你把所有玩家告诉你的数…

appium + python 搭建

appium python 1、安装jdk地址&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html配置&#xff1a;系统变量新建JAVA_HOME,输入jdk安装路径&#xff0c;如&#xff1a;C:\Program Files\Java\jdk1.8.0_201。系统变量新建CLAS…

ubuntu 12.04 /sbin/ldconfig.real: /usr/local/lib/*.so.8 不是符号连接 解决办法

最近在ubuntu12.04下使用sudo apt-get install安装软件的时候最后总会报这种错误。建立硬连接即可解决&#xff1a; sudo ln -sf /usr/local/lib/libprotobuf.so.8.0.0 /usr/local/lib/libprotobuf.so.8 截图上传没成功&#xff0c;麻烦。 在弄一次。

Linux GCC常用命令

1简介 2简单编译 2.1预处理 2.2编译为汇编代码(Compilation) 2.3汇编(Assembly) 2.4连接(Linking) 3多个程序文件的编译 4检错 5库文件连接 5.1编译成可执行文件 5.2链接 5.3强制链接时使用静态链接库 1简介 GCC 的意思也只是 GNU C Compiler 而已。经过了这么多年的发展&#…

JDK 9 @不建议使用的注释增强功能

在帖子中&#xff0c; Deprecated可能会是什么样子&#xff1f; &#xff0c;我当时使用JEP 277 &#xff08;“增强的弃用”&#xff09;的描述来指导创建增强的自定义Deprecated注释。 但是&#xff0c;自从发布该文章以来&#xff0c;JEP 277进行了重大更改。该文章总结了J…

数据结构和算法基础之冒泡排序

/// <summary>/// 冒泡排序/// </summary>/// <param name"arry"></param>public static void BubbleSort(int[] arry){for (int i 0; i < arry.Length; i)//外层循环控制排序趟数{for(int j0;j<arry.Length-i-1;j)//内层循环控制每一…

Java Web应用程序的SecureLogin

不&#xff0c;标题中没有缺少空格。 这与安全登录无关 &#xff0c;而与安全顾问Egor Homakov开发的SecureLogin协议有关&#xff0c;该协议因在没有权限的情况下致力于掌握 Rails项目而闻名 。 SecureLogin协议非常有趣&#xff0c;因为它不依赖任何中央方&#xff08;例如&…

最通俗的CRC校验原理剖析

以下内容摘自笔者即将出版的最新著作《深入理解计算机网络》一书。本书将于12月底出版上市&#xff0c;敬请留意&#xff01;&#xff01; 本书原始目录参见此文&#xff1a;http://winda.blog.51cto.com/55153/1063878 5.3.2 循环冗余校验检错方案 上节介绍的奇偶校验码&#…

SpringMVC 上传文件and过滤器

SpringMVC提供了一个MultipartResolver接口用来实现文件上传&#xff0c;并使用Commons FileUpload技术实现了一个该接口的实现类CommonsMultipartResolver。如果要在SpringMVC中实现文件上传功能&#xff0c;就可以在springmvc.xml中配置MultipartResolver接口的实现类。 以下…

guava 集合转换_Guava的Collections2:过滤和转换Java集合

guava 集合转换Groovy的便利之一是能够通过Groovy的闭包支持轻松地对集合执行过滤和转换操作。 Guava将对集合的过滤和转换引入标准Java&#xff0c;这是本文的主题。 Guava的Collections2类具有两个公共方法&#xff0c;这两个方法都是静态的。 方法filter&#xff08;Collec…