货物运输系统概述
我们要为一个货运公司开发新软件。最初有3个基本功能
1. 跟踪顾客货物的关键装卸事件
2. 对货物进行事先预约
3. 当货物在装卸中抵达某个点时,自动向顾客发送发票
这个模型组织了领域知识,并为开发团队提供了一种语言。我们可以给出如下陈述:
1. 多个cunsotmer涉及到一个Cargo,每个Customer充当一种不同的role。
2. 已经指定了提供目标
3. 满足Specification的一系列Carrier Movement将实现提货目标
模型中的每个对象都有清晰的含义:
1. Handling Event装卸事件是发生在Cargo上的一种离散活动,例如货物装船或者出关。这个类也许可以细分为一个包含不同种类事件的层次结构,例如装货,卸货,或者收货人提货等。
2. Delivery Specification提货规格定义了一个提货目标,它至少包含一个目的地和一个抵达时间,但是还可以更加复杂。这个类遵循规格模式(参加第9章)
3. Delivery Specification的职责本来可以由Cargo对象来承担,但是把它抽象出来至少可以有3个好处:
a.如果没有Delivery Specification,所有与指定提货目标有关的属性和关联就都要由 Cargo对象来负责。这会使Cargo变得散乱,理解或修改会更困难。
b.当把模型作为一个整体来说明时,这种抽象使我们可以很容易且安全地把细节隐藏起来。例如,Delivery Specification可能还封装了其他的标准,但是在这个细节层次上,我们并不需要把它暴露出来。这个图是告诉读者,这里有一个提货规格,但是它的细节并不重要,无需考虑。
c.这个模型更富有表达能力。添加Delivery Specification把问题说得很明白了:运输Cargo的实际方式可以自由确定,但是它必须达到由Delivery Specification给出目标。
4. role将顾客在一次运输中扮演的不同角色区分开来。例如,有“托运人”、“收货人”等。对于一件特定的Cargo,一个顾客只能扮演一个角色,因此Customer和Cargo之间的关联成为限定的多对一关联,而不是多对多关联。角色可以实现为一个简单的字符串,如果还需要其他行为的话,也可以实现为一个类。
5. Carrier Movement代表一个特定的Carrier(例如一辆卡车或者一艘货轮)从一个Location到另一个地点的转移。Cargo就可以装上Carrier, 在一个或者多个Carrier Movement期间,从一个地方转移到另一个地方。
6. Delivery Specification描述目标,而Delivery History(提货历史)则反映了发生在Cargo上的实际行为。通过分析最后一次装卸或卸货对应的Carrier Movement的目的地,我们可以从Delivery History对象计算出Cargo的当前位置。如果提货成功的话,那么Delivery History最后应该满足Delivery Specification的目标。、
区分实体和值对象
1. Cunstomer
我们从一个容易一些的对象开始。一个Customer对象代表一个人活着一个公司,在通常意义下这是一个实体。
2. Cargo
两个同样的货箱必须区分,因此Cargo货物对象是实体。
3. Handling Event(装卸事件)和Carrier Movement(承运人运输)
我们关心每个个体事件,因为我们需要这些信息来跟踪进展。这些事件反映了现实世界中发生的事件,通常是不可互换的,因此它们是实体。每个Carrier Movement都会从运输计划中得到一个代码作为标识。
4. Location(地点)
名称相同的两个地点不是同一个地点。
5. Delivery History
Delivery History有些复杂。Delivery History是不可互换的,因此它们是实体。但是Delivery History与它发运的Cargo具有一对一关联,因此它实际上并没有自己的标识。它的标识是从所属的Cargo借用过来的。用聚合来建模将使问题更加清晰。
6. Delivery Specification(提货规格)
虽然Delivery Specification代表Cargo的目标,但是这个抽象并不依赖于Cargo。它实际上描述了某些Delivery History的假象状态。我们希望与一件Cargo连接的Delivery History最终能够满足与它连接的Delivery Specification;但是,虽然二者的历史在最开始时都相同,但它们不能共享同一个Delivery History。Delivery Specification是值对象。
7. Role(角色)和其他属性
Role角色提供了与其限定的关联有关的信息,但是它没有历史的或连续的要求。它是一个值对象,可以在不同的Cargo/Customer关联中进行共享。
其他属性,如时间戳或者名字,都是值对象。
聚合的边界
选择仓储
设计中有5个实体是聚合根。我们只需考虑这几个对象,因为其他对象都不允许有仓储。
为了决定哪些候选者确实需要仓储,我们必须重温一下应用需求。为了在Booking Application中进行预约,用户需要选择不同角色(托运人、收货人等)的Customer。因此,我们需要一个Customer Repository。用户还要查找Location来指定Cargo的目的地,因此Location Repository也是需要的。
Activity Logging Application需要允许用户查找装载了给定Cargo的Carrier Movement,因此我们需要一个Carrier Movement Repository。用户需要告诉系统哪个Cargo已经装载,因此还需要一个Cargo Repository。
对象的创建
我们将每个要创建的对象放到工厂里,然后传入需要的参数,然后开始创建。
遗憾的是,故事并不像这么简单。Cargo到Delivery History到Handling Event再回到Cargo的这个引用循环把事例创建问题搞复杂了。Delivery History持有一个与其Cargo相关的Handling Event集合,而新创建的Handling Event对象必须在同一个事物中加入到这个集合中来。如果不创建这种反向指针,对象将使不一致的,如下图:
我们会发现一个动作会牵扯到两个操作,那么这个动作就是事物的,这样有出现一定的问题,下面我们给出另一种解决办法。
实际上,当把数据库作为底层技术时,我们可以设法用内部执行的查询来模拟对象集合。使用查询而不是集合使我们能更容易地维护Cargo和Handling Event之间的循环引用的一致性。
我们为Handling Event增加一个仓储来承担查询的职责。Handling Event Repository将提供一个根据给定Cargo查询其相关Event的功能。此外,仓储还可以为某些特定的问题进行优化,使其获得更高的查询效率。例如,为了推断Cargo的当前状态,我们需要根据Delivery History来查找最后一次报告的装货和卸货。如果这个访问路径使用非常频繁,那么我们可以设计一个专门的查询,仅仅返回与最后一次报告相关的Handling Event。如果我们希望用一个查询来得出某个给定Carrier Movement中装载的所有Cargo,也只要增加一个查询就行了。
- 大小: 108.8 KB
- 大小: 186.8 KB
- 大小: 167 KB
- 大小: 126.2 KB
分享到:
相关推荐
Cargo案例是DDD中的一个经典示例,它模拟了一个物流系统中货物运输的过程,涵盖了从接收订单到货物送达目的地的整个流程。 在Cargo案例中,我们首先遇到的是核心领域概念——Cargo(货物)。Cargo作为业务实体,...
经过两年的不活动后,可以使用新版本PHP DDD Cargo Sample [2015/12/07]。 新版本是使用尖端技术完全重写的货物样品。 tl; dr 点击 :grinning_face_with_smiling_eyes: 有什么新功能? PHP 7具有严格的标量类型...
ddd-cargo 领域驱动货物需求示例 需求-节选自《领域驱动设计第7章》 假设我们正在为一家货运公司开发新的软件,最初的需求包括三项基本功能: 事先预约货物 跟踪客户货物的主要处理流程 当货物到达其处理过程中的...
当我们谈论在Maven下使用Cargo实现自动化部署到Tomcat7服务器时,我们主要涉及以下几个知识点: 1. Maven的配置和使用: Maven是一个项目管理工具,主要用于Java项目。它使用了一种名为POM(Project Object Model...
Cargo是Maven的一个强大插件,它主要用于容器管理和部署Web应用程序。Cargo插件可以让你轻松地在各种Web服务器上启动、停止、配置甚至部署你的应用,而无需手动操作。在这个"cargo远程部署web项目资料"中,我们将会...
1.安装make: cargo install cargo-make 2.将压缩包释放到某个文件夹下,并进入该文件夹 3.启动一个命令窗口,使用cargo make build构建项目 4.使用cargo make serve启动项目 5.启动另外一个命令窗口,使用cargo make...
Cargo 浏览器是一款独特的网页浏览工具,设计思想聚焦于极简主义,旨在提供一个几乎无用户界面的体验。这款浏览器特别强调通过键盘快捷键来操控,以减少视觉干扰,帮助用户专注于网页内容本身,避免不必要的功能分散...
cargo-outdated, 用于显示 Rust 依赖项什么时候过期的cargo子命令 货物过期 Linux: 用于显示 Rust 依赖项什么时候过期的cargo子命令cargo-outdated 用于显示依赖项是否有更新的版本。工作原理cargo-outdated的功能很...
在IT行业中,管理和部署Web应用程序是一项关键任务,而Maven Cargo插件则为开发者提供了一个强大的工具,使得在Tomcat6上部署应用变得更加便捷。Cargo是一个用于自动化服务器和容器管理的Java库,它允许开发者在多种...
Cargo is the Rust package manager. Cargo downloads your Rust package’s dependencies,compiles your packages, makes distributable packages, and uploads them to crates.io, the Rust community’s package ...
cargo-lipo, Cargo子命令自动为iOS创建通用库 cargo lipo 为你的iOS应用程序自动创建一个 cargo lipo 子命令,它将自动创建一个通用库。用法你可以从任何地方运行 cargo,现在可以运行 cargo lipo 或者 cargo lipo -...
《Cargo Optimizer Professional》是一款专为物流行业设计的装柜优化软件,旨在提高运输效率,降低运输成本,确保货物安全。这款软件的核心功能体现在以下几个方面: 1. **最大货物尺寸限制**:在进行装载规划时,...
cargo-ant-1.1.2.jar 给需要的朋友。
Cargo Two SF
标题 "cargo-core-api-util-1.4.11.zip" 提示我们这是一个关于 Cargo 的核心 API 工具库的版本,版本号为 1.4.11。Cargo 是 Rust 编程语言的一个关键组件,它负责构建、依赖管理和分发 Rust 项目。Cargo API 实用...
Cargo 是Rust的 包经理。Cargo 会下载您 Rust 的包依赖项,编译您的包,生成可分发的包,并将 它们上传到crates.io - Rust 社区的包注册表。 这是官方的翻译版,并不保证翻译的完整准确,希望能想学习rust的朋友一点...
Cargo是Rust编程语言的包管理器和构建工具,它负责管理项目的依赖关系、构建代码、生成可分发的包以及发布到crates.io——Rust社区的官方包注册库。Cargo手册是官方文档,提供了关于如何使用Cargo的详细指南和参考...
Build a bridge using your mouse and available budget. When it is done click "Test your bridge". Your workers will use it to get items located at the other side of valley, and bring them back ...
cargo jetty-7-and-onwards-deployer-1.6.5,自动上传部署