DDD项目的Maven模块布局(Maven module layout for DDD projects)

编程入门 行业动态 更新时间:2024-10-05 15:35:05
DDD项目的Maven模块布局(Maven module layout for DDD projects)

在进行DDD项目时如何布局Maven模块? 您是否将所有图层(演示文稿,应用程序,域,基础结构)放在一个模块中,或者您是否为每个图层创建了一个单独模块的多模块布局? 或完全不同的东西?

我注意到由Domain Language和Citerus公司开发的DDD示例应用程序使用单个Maven模块,每个层都作为该模块内的单独Java包。 这是既定的最佳实践,还是应该考虑更精细的模块布局?

How do you layout your Maven modules when doing DDD projects? Do you fit all layers (presentation, application, domain, infrastructure) in a single module, or do you create a multi-module layout with a separate module for each layer? Or something else entirely?

I notice that the DDD Sample App, developed by the companies Domain Language and Citerus, uses a single Maven module, with each layer as a separate Java package inside that module. Is this the established best practice, or should I consider a more granular module layout?

最满意答案

通常,模块分离和封装是部署和开发实用性的问题。 还有谁会要求代码? 如果我想更改功能Y,是否在X包中?

警告:示例应用程序打包为单个应用程序,以便于作为学习工具使用。 但是这里有一些推荐使用它作为例子,假装它是真实的。 为了说明的目的,我会在真空中做出一些假设,但DDD的负责任的实践者会抓住一个域专家并采访开发团队来验证关于域,上下文边界和这些上下文之间关系的任何和所有假设。 你不能单独做DDD。

获得正确的建模

第一步是专注于建立正确的建模和定义的上下文边界。 我不担心基础设施层,因为我担心域内的各种上下文及其模型。 示例应用程序中的关键区别是这些不同上下文之间的区别,此应用程序中有三种上下文

预订 路由 第三方供应商/港口/船舶等

如果你注意到那些被root java包清楚地分开了

se.citerus.dddsample com.pathfinder com.aggregator

层次主要是为了促进这些上下文之间的通信,并将基础架构问题与域名工作分开,使测试更容易,域责任更加简单。 基础设施很重要,但是示例应用程序在这里使用XML而JMS存在并且在那里进行休眠的事实是对域建模的二次关注。

示例应用程序使这种分离非常清晰,很容易看到聚合根的位置:

货物 HandlingEvent 位置 航程

打破Aggegrate Roots的java软件包是很有实践的。 一条腿在货物集合之外没有任何意义,一个时间表意味着Voyage集合之外没有任何东西,一个HandlingHistory在HandingEvent集合之外没有任何意义。 保持域模型与基础架构隔离和可测试是一种很好的做法。 但是你可能不会将解耦扩展到模块级别。 说一个jar中的所有域对象和另一个jar中的所有基础结构都不是常规。 开发和版本负担可能会变得很痛苦。

识别有界上下文

关键是各种上下文的模型是如何分离/不共享的 。 在预订环境中,路线是具有一组腿的行程。 在路由域中,它是真正的计算机科学意义上的图形,因此域可以使用经过充分研究的图形遍历算法来解决路由问题。

两种情况下,预订和路由都处于紧密的合作伙伴关系中 ,它们都在两个边缘和节点以及行程和腿部模型之间保持共享接口。 在TransRPath成为行程的ExternalRoutingService中管理模型之间保持此转换。 显然,这是一个非常关键的集成点,应该在测试中得到很好的覆盖,并通过持续集成进行管理

另一个上下文是第三方集成,向应用程序报告关于货物状态的HandingEvents。 这是通过名为Published Language的模式实现的。 简而言之,我们不关心第三方的货物模型是什么样的,只要他们按照我们的Publish XML规范HandlingReport向我们报告处理事件。

第三方上下文和预订域之间的关系称为Conformist ,它们按照我们定义的规范提交数据,我们不会改变我们的模型以使它们更容易。 他们的责任在于他们与我们相符。 也就是说,这是我对这种情况的猜测,可能是那里有一个非常重要的供应商,事实上他们确定的是XML模型而不是我们。 只有对虚构团队的采访才能真正体现出这一点。

所以在汇总组中所有与聚合相关的类都很紧密(也许是相同的包)。 明确定义上下文边界并确保有明确的集成点,定义上下文伙伴关系,共享内核,已发布语言,开放主机服务,符合性等的关系。

基于该示例,我们可以将各种上下文打包在单独的maven模块中,用于货物预订,路径路径查找和处理事件聚合。 如果根据开发方法和团队组织的实际情况,这是有道理的,并且只有在这种情况下才是这样。

在有界上下文中查找模块

获取您的上下文边界权限。 将您的聚合正确定义为漂亮的垂直。 减少耦合以清除定义良好的接口。

在您的上下文中查找模块。 它们是单独模块的最自然候选者,分离可能有助于更严格地执行和记录上下文边界。 然而,像软件设计这么多,它并不是一个硬性规则,而是真正取决于具体案例。 我可以设想并且已经看到/编写了具有不同写模型和读模型的应用程序(对于报告来说,认为规范化和非规范化)以及每个应用程序的上下文,对于实际问题,可能仍然打包在单个模块中。

另外一点要警惕在上下文之间共享聚合根 ,这是DDD共享内核模式,应该非常谨慎地使用,因为它可以快速地进入一个不能很好地满足任何上下文需求的大型混乱域模型。 请注意,示例应用程序不在RoutingService和BookingService之间共享模型。 将所有域的聚合根植入单个模块可能会无意中鼓励这种做法。

Generally Module separation and packaging is a question of deployment and development practicalities. Who else is going to require the code? If I want to change functionality Y, is it all in package X?

Caveat: the sample app is packaged as single app to make it easy to consume as a learning tool. But here are some reccomendations using it as a example and pretending it's real. I will make some assumptions about it in a vacuum for illustrative purposes, but a responsible practitioner of DDD would grab a Domain Expert and interview the development team to validate any and all assumptions about the domain, context boundaries and the relationship between those contexts. You can't do DDD alone.

Get the Modeling Right

Step one would be to focus on getting the modeling right and the context boundaries defined. I would not worry about infrastructure layers so much as I would worry about the various Contexts and their models within the domain. The critical distinctions in the Sample App are those between these different contexts, there are three contexts in this app

Booking Routing 3rd Party Vendors/Ports/Ships etc

If you notice those are clearly separated by the root java packages

se.citerus.dddsample com.pathfinder com.aggregator

The Layers are mostly to facilitate communication between these contexts and to separate infrastructure concerns from the domain work making testing easier and domain responsibilities more plain. Infrastructure is important but the fact the sample app is using XML here and JMS there and hibernate over there are secondary concerns to the domain modeling going on.

The example app makes this separation very clear, it's easy to see where the Aggregates Roots are:

Cargo HandlingEvent Location Voyage

Breaking up the java packages by Aggegrate Roots is solid practice. A Leg means nothing outside the Cargo aggregate, a schedule means nothing outside of the Voyage aggregate, a HandlingHistory means nothing outside the HandingEvent aggregate. Keeping the domain model isolated from and testable without infrastructure is a good practice. But you'd probably not extend that decoupling to the module level. It would not be a rule to say have all your domain objects in one jar and all your infrastructure in another. Development and version burdens might become painful.

Identify Bounded Contexts

The key is how the various context's models are separate/unshared. In the booking context a route is an Itinerary with a set of Legs. In the routing domain it's a Graph in the real Computer Science sense of the word, so that domain can solve routing problems using well studied graph traversal algorithms right out of your Algorithms class from college.

The two contexts Booking and Routing are in a close Partnership where they both maintain a shared interface between the two models of Edges and Nodes and Itineraries and Legs. This translation is maintained between model is managed in the ExternalRoutingService that's where a TransitPath becomes an Itinerary. Obviously that is a very critical integration point that should be well covered in tests and managed through Continuous Integration

The other context is 3rd party integration to report HandingEvents to the app about cargo status. This is achieved through a pattern called Published Language. In a nutshell we don't care what the cargo models of 3rd parties look like, as long as they report handling events to us following our Publish XML spec HandlingReport.

The relationship between the 3rd party context and the Booking Domain is called Conformist, they submit data following our defined specification we do not alter our models to make this easier for them. The burden is on them to conform to us. That said, that's my guess of the situation, it could be that there's a very important vendor out there and they are in fact defined the XML model not us. Only interviews with the fictional team could really characterize that.

So in summary group all classes related to an Aggregate closely (same package perhaps). Define Context boundaries clearly and ensure there's a clear integration point, define the relationship of the contexts Partnership, Shared Kernel, Published Language, Open Host Service, Conformist, etc.

Based on that in the example we could probably package the various contexts in separate maven modules for Cargo Booking, Route Path Finding and HandlingEvent Aggregation. If that made sense given the practicals of the development methodology and team organization, and only if that was the case.

Look for Modules in Bounded Contexts

Get your context boundaries rights. Define your Aggregates correctly into nice verticals. Reduce coupling to clear well defined interfaces.

Look for modules in your contexts. They are the most natural candidates for separate modules and the separation may help more strictly enforce and document context boundaries. However like so much of software design it's not a hard and fast rule and will really depend on the specific case. I can envision and have seen/wrote applications that have different write models and read models (think normalized and denormalized for say reporting) and contexts for each, that for practical matters might still be packaged in a single module.

Another point be wary of sharing Aggregate Roots between contexts, this is the DDD shared kernel pattern and should be used very sparingly as it can quickly spiral into a large messy domain model that doesn't serve the needs of any context well. Note that the example app does not share models between the RoutingService and the BookingService. Putting all of domain's aggregates roots in a single module might inadvertently encourage this practice.

更多推荐

本文发布于:2023-08-07 11:11:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1464112.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:布局   模块   项目   Maven   DDD

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!