我正在开发一个使用 Spring Data JPA 访问数据层的Spring应用程序。
所以基本上我有 n实体类和 n相关的存储库类来访问与实体类关联的数据库表的数据,如下所示:
<a href="#" onclick="downloadURL('path/of/mp3/file')">Download File</a>
所以基本上这样我就可以通过** n个存储库类访问 n个域类。
我可以将这些 n个域类划分为属于一个共同概念的子集。
例如,我可以拥有以下实体类: Room , RoomTipology RoomRate 和 RoomPicture 会议室概念。
所以我将有以下服务类: RoomDAO , RoomTipologyDAO , RoomRateDAO 和 RoomPictureDAO 。
它工作正常,但我想采用更强大的 DOMAIN-DRIVEN DESIGN 架构。
所以我发现这篇有趣的文章是关于如何在Spring应用程序中获得 DOMAIN-DRIVEN DESIGN :http://static.olivergierke.de/lectures/ddd-and-spring/
阅读上一篇文章时说:
Repository - Spring组件,通常是Spring Data存储库 接口。可以依赖实体和值对象,居中 围绕聚合根的实体。
究竟意味着什么?这意味着我可以创建聚合根类(例如 RoomAggregation ,将 Room , RoomTipology <聚合在一起strong> RoomRate 和 RoomPicture 实体类)使用 @Embedded 注释。
或者什么?
使用 Spring Data JPA 在我的应用程序中获取 DOMAIN-DRIVEN DESIGN 的优秀架构解决方案是什么?
答案 0 :(得分:2)
您不会将新类创建为聚合。相反,你选择一个现有的。通常它会呈现自己,在您的示例中可能是RoomRepository
所以你会有一个房间存储库(Rooms
或RoomPicture
)。您可以使用它来保存和加载房间,包括按照您需要的各种标准查找房间。
为了访问(和操作)例如Room
您加载RoomPicture
导航到Booking
操纵它并保存您的JPA会话,这实质上意味着您正在修改间。
如果您选择实体之间的简单导航(@OneToMany和团伙)或@Embedded不受您选择的聚合的影响,除了您没有从聚合到另一个聚合的直接引用。因此,如果您还有Booking
作为AggregateRoot,则roomId
将包含<project name = "HK" default = "test" basedir = ".">
<property name = "srcdir" location = "src" />
<property name = "testdir" location = "testclasses" />
<property name="libdir" value="lib" />
<path id = "classpath.test">
<fileset dir="${libdir}" includes="**/*.jar"/>
</path>
<target name="clean" description="Removes previous build">
<delete dir="${testdir}" />
</target>
<target name="compile" description="Compiles java source code">
<mkdir dir="${testdir}" />
<javac srcdir="${srcdir}" destdir="${testdir}" debug="true" classpathref="classpath.test" includeantruntime="false"/>
</target>
<target name = "test" depends = "compile">
<junit printsummary="yes" haltonfailure="yes" showoutput="yes" description="yes" failureproperty="true">
<classpath location="${libdir}/junit4.jar" />
<classpath location="${libdir}/hamcrest-core-1.3.jar" />
<classpath location = "${testdir}/adviceTest"/>
<formatter type="xml"/>
<test name = "TestRunner" todir="./junitreport" />
</junit>
</target>
<target name="junitreport" description="Create a report for the rest result">
<junitreport todir="./junitreport">
<fileset dir="./junitreport">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="./junithtml/html" />
</junitreport>
</target>
,并且您将使用会议室存储库来查看会议室。
答案 1 :(得分:0)
首先,忘记Spring Data JPA或任何其他持久性机制。领域驱动设计是关于在分析中对域进行建模,您的具体案例是关于对某些酒店子域进行建模,并且根本不担心持久性。这是与酒店无关的另一个完全不同的问题,而是持久性(另一个领域或有限背景)。
您必须考虑用例而不是概念,以便我们可以识别行为,这样就可以检测不变量,从而使我们能够考虑围绕必须保持一致的边界建立边界。
请记住按照Vaughn的建议对小聚合进行建模,因此您对拥有大聚合的想法听起来不太好。酒店经理可以实例化房间以实际代表房间并给它一个数字和其他一些东西。现在价格和图片怎么样?让我们以价格为例,让我问你,房间真的知道它的价格是多少吗?房价是否充满活力并且完全受到许多其他变量(如日期)的影响?那么为什么要在Room聚合中确定一个比率呢?我们说房间在现实世界中没有责任这样的责任,价格受到房间边界以外的几个条件的影响。因此,如果我们在哲学术语中谈论,那么费率对于房间来说是完全偶然的,不是必需的。
另一个Aggregate是PriceList,它的Aggregate Root具有相同的名称。所以你可以在特定的日期询问价格表,房间的价格(标准,豪华......),它会知道价格:) 根据您对所有这些内容进行建模的方式,这可能属于作为微服务公开的定价有界上下文,但不要担心 关于它。 PriceList是与房间完全隔离的另一个聚合,并通过其概念标识(房间号)来引用房间。
同样适用于RoomPicture。在这种特殊情况下,认为处理专辑图片的部门/区域/员工可能不是经理,而是具有不同角色的其他人,并创建具有自己生命周期的房间相册。
作为结论,你最终会得到这些聚合:
间 价目单价目表 专辑
请记住,所有这些都断开了关联,指的是房间号码(不是房间),每个房间都有不变的自己的边界。 在一个巨大的应用程序中,你可能会让每个人都生活在自己有限的环境中。
记住Room不会对其图片专辑及其价目表强制执行任何不变量。
希望它有所帮助, 塞巴斯蒂安。
答案 2 :(得分:0)
正如@Sebastian已经详细阐述的那样,当你正在DDDing你的应用程序时,坚持应该是最不用担心的事情。 你没有将你的实体与数据库结合在一起,否则你最终会得到一个非常数据库驱动的应用程序,这个应用程序与ddd搞混在一起,它在清晰的代码和美感方面都不会出现。 在youtube上有一个很好的演示文稿名为Domain Driven Design for Database Driven Mind请查看。
其次,你的大部分DAO应该重构为Value Objects,你将业务逻辑转储到Value Objects和Entities,最后当你需要担心存储这些东西时,只需要使用数据映射器层。
根据经验说,正确完成的价值对象可以减轻90%的正确应用设计权重。