描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787121224485
著译俱佳 ThoughtWorks资深咨询师倾力译校
完整涵盖DDD各方面知识 提供大量示例代码
案例贯穿全书 理论与实践紧密衔接之典范
架构师、程序员境界提升不可或缺之必选书目
领域驱动设计(DDD)是教我们如何做好软件的,同时也是教我们如何使用面向对象技术的。它为我们提供了设计软件的全新视角,同时也给开发者留下了一大难题:如何将领域驱动设计付诸实践?Vaughn Vernon 的这本《实现领域驱动设计》为我们给出了全面的解答。
《实现领域驱动设计》分别从战略和战术层面详尽地讨论了如何实现DDD,其中包含了大量的实践、设计准则和对一些问题的折中性讨论。《实现领域驱动设计》共分为14 章,在DDD 战略部分,《实现领域驱动设计》向我们讲解了领域、限界上下文、上下文映射图和架构等内容,战术部分包括实体、值对象、领域服务、领域事件、聚合和资源库等内容。一个虚构的案例研究贯穿全书,这对于实例讲解DDD 实现来说非常有用。
《实现领域驱动设计》在DDD 的思想和实现之间建立起了一座桥梁,架构师和程序员均可阅读,同时也可以作为一本DDD 参考书。
前言 xxi
致谢 xxxi
关于作者 xxxv
如何使用本书xxxvii
第1章 DDD入门
我能DDD吗?
为什么我们需要DDD
如何DDD
使用DDD的业务价值
1你获得了一个非常有用的领域模型
2你的业务得到了更准确的定义和理解
3领域专家可以为软件设计做出贡献
4更好的用户体验
5清晰的模型边界
6更好的企业架构
7敏捷、迭代式和持续建模
8使用战略和战术新工具
实施DDD所面临的挑战
虚构的案例,真实的实践
本章小结
第2章 领域、子域和限界上下文
总览
工作中的子域和限界上下文
将关注点放在核心域上
战略设计为什么重要
现实世界中领域和子域
理解限界上下文
限界上下文不仅仅只包含模型
限界上下文的大小
与技术组件保持一致
示例上下文
协作上下文
身份与访问上下文
敏捷项目管理上下文
本章小结
第3章 上下文映射图
上下文映射图为什么重要
绘制上下文映射图
产品和组织关系
映射3个示例限界上下文
本章小结
第4章 架构
采访一个成功的CIO
分层
依赖倒置原则
六边形架构(端口与适配器)
面向服务架构
REST
REST作为一种架构风格
RESTful HTTP服务器的关键方面
RESTful HTTP客户端的关键方面
REST和DDD
为什么是REST?
命令和查询职责分离——CQRS
CQRS的各个方面
处理具有终一致性的查询模型
事件驱动架构
管道和过滤器
长时处理过程(也叫Saga)
事件源
数据网织和基于网格的分布式计算
数据复制
事件驱动网织和领域事件
持续查询
分布式处理
本章小结
第5章 实体
为什么使用实体
标识
用户提供标识
应用程序生成标识
持久化机制生成标识
另一个限界上下文提供标识
标识生成时间
委派标识
标识稳定性
发现实体及其本质特征
揭开实体及其本质特征的神秘面纱
挖掘实体的关键行为
角色和职责
创建实体
验证
跟踪变化
本章小结
第6章 值对象
值对象的特征
度量或描述
不变性
概念整体
可替换性
值对象相等性
无副作用行为
小化集成
用值对象表示标准类型
测试值对象
实现
持久化值对象
拒绝由数据建模泄漏带来的不利影响
ORM与单个值对象
多个值对象序列化到单个列中
使用数据库实体保存多个值对象
使用联合表保存多个值对象
ORM与枚举状态对象
本章小结
第7章 领域服务
什么是领域服务(首先,什么不是领域服务)
请确定你是否需要一个领域服务
建模领域服务
独立接口有必要吗
一个计算过程
转换服务
为领域服务创建一个迷你层
测试领域服务
本章小结
第8章 领域事件
何时/为什么使用领域事件
建模领域事件
创建具有聚合特征的领域事件
身份标识
从领域模型中发布领域事件
发送方
订阅方
向远程限界上下文发布领域事件
消息设施的一致性
自治服务和系统
容许时延
事件存储
转发存储事件的架构风格
以REST资源的方式发布事件通知
通过消息中间件发布事件通知
实现
发布NotificationLog
发布基于消息的事件通知
本章小结
第9章 模块
通过模块完成设计
模块的基本命名规范
领域模型的命名规范
敏捷项目管理上下文中的模块
其他层中的模块
先考虑模块,再是限界上下文
本章小结
第10章 聚合
在Scrum核心领域中使用聚合
次尝试:臃肿的聚合
第二次尝试:多个聚合
原则:在一致性边界之内建模真正的不变条件
原则:设计小聚合
不要相信每一个用例
原则:通过标识引用其他聚合
通过标识引用使多个聚合协同工作
建模对象导航性
可伸缩性和分布式
原则:在边界之外使用终一致性
谁的任务?
打破原则的理由
理由之一:方便用户界面
理由之二:缺乏技术机制
理由之三:全局事务
理由之四:查询性能
遵循原则
通过发现,深入理解
重新思考设计
估算聚合成本
常见用例场景
内存消耗
探索另外的设计
实现终一致性
这是Scrum团队成员的任务吗?
决定的时候到了
实现
创建具有标识的根实体
优先使用值对象
使用迪米特法则和“告诉而非询问”原则
乐观并发
避免依赖注入
本章小结
第11章 工厂
领域模型中的工厂
聚合根中的工厂方法
创建CalendarEntry实例
创建Discussion实例
领域服务中的工厂
本章小结
第12章 资源库
面向集合资源库
Hibernate实现
TopLink实现
面向持久化资源库
Coherence实现
MongoDB实现
额外的行为
管理事务
警告
类型层级
资源库 vs 数据访问对象(DAO)
测试资源库
以内存实现进行测试
本章小结
第13章 集成限界上下文
集成基础知识
分布式系统之间存在根本性区别
跨系统边界交换信息
通过REST资源集成限界上下文
实现REST资源
使用防腐层实现REST客户端
通过消息集成限界上下文
从Scrum的产品负责人和团队成员处得到持续通知
你能处理这样的职责吗?
长时处理过程,以及避免职责
长时处理过程的状态机和超时跟踪器
设计一个更复杂的长时处理过程
当消息机制或你的系统不可用时
本章小结
第14章 应用程序
用户界面
渲染领域对象
渲染数据传输对象
使用调停者发布聚合的内部状态
通过领域负载对象渲染聚合实例
聚合实例的状态展现
用例优化资源库查询
处理不同类型的客户端
渲染适配器以及处理用户编辑
应用服务
示例应用服务
解耦服务输出
组合多个限界上下文
基础设施
企业组件容器
本章小结
附录A 聚合与事件源:A ES
应用服务内部
命令处理器
Lambda语法
并发控制
A ES所带来的结构自由性
性能
实现事件存储
关系型持久化
BLOB持久化
专注的聚合
读模型投射
与聚合设计一道使用
增强事件
工具和模式
事件序列器
事件不变性
值对象
协议生成
单元测试和需求规范
事件源和函数式语言
参考文献
所有的计算都表明它不工作,的做法是:使其工作。 –Pierre-Georges Latécoère早期法国航空企业家
是的,我们将使其工作。然而,在软件开发过程中采用领域驱动设计却是困难的。即便是有能力的开发者,也很难找到实现领域驱动设计的正确方法。
起飞,着陆
在我小的时候,我的父亲学习过驾驶小型飞机。我们经常会全家出去飞行,有时会飞到另一个机场,在那里吃过午饭后再返回。当父亲时间有限而他依然想飞时,父亲便带上我一起在机场上空盘旋,起飞,着陆,再起飞,再着陆。
也会有些长途飞行,这时我们会带上一张由父亲先前绘制好的路线图。我们几个小孩便当起了领航员:将图上的标志对应着陆地上的地标,以确保我们没有跑偏航线。这是一件很有趣的事情,因为要识别远在地面上的物体是很有挑战性的。事实上,我敢肯定父亲根本不用我们领航便知道我们处于什么方位–他能看到仪表盘上的所有信息,并且他拥有仪表飞行执照。
空中的景观的确改变了我的视野。不时地,父亲和我会飞过我们乡下的房子。在几百英尺的高空中,我体会到了另一种“家”的概念,而这在之前是没有过的。当我们飞过自家的房子时,母亲和我的姐妹们便会跑到院子里向我们挥手。我知道那是她们,即便我看不清楚她们是谁。谈话肯定是不行的,连大声喊都不行,她们是听不见的。我还可以看到将我家和外面公路分开的护栏,平时我们会像走平衡木一样在护栏上面走来走去。从空中看,它们就像被细心编排过的小树枝一样。我们
家的院子很大,每每到了夏天,我都会开着割草机一排一排地修理院子里的草坪。而在空中时,我只能看到一片绿色,小草的叶子肯定是看不清楚的。
我喜欢在空中的时刻,直到现在我还不时回想起这些时刻,好像那个降落飞
机的黄昏就发生在不久以前一样。虽然如此,在地面上的感觉依然是无法取代的,
因为它给我一种脚踏实地的感觉。
着陆于领域驱动设计
一开始接触领域驱动设计( DDD)就像一个小孩之于飞行一样。天空中的景色是令人惊叹的,但有时我们却因为过于陌生而搞不明白它们到底是什么。要从甲地到乙地显得如此的遥远。然而, DDD的“成年人 “们却总知道他们所处的方位,因为他们在很早之前便绘制好了路线图,并且能够完全按照仪表进行相应的操作。而还有很多人找不到”在地面上“的感觉,此时我们需要的是”稳定着陆“的能力,然后找到一张地图给我们指引方向。
Eric Evans的《领域驱动设计:软件核心复杂性应对之道》是一本经得住时间考验的经典之作。我坚定地相信,在接下来的几十年里,本书依然会是开发者的实用指导。和其他模式一样,该书为我们建立起了一种高屋建瓴式的宽阔视野。然而,对于如何实现 DDD,我们可能将面对更多的挑战。通常来说,我们更渴望看到一些具体的例子。
我的目标之一便是帮助你来一个”软着陆“,保全飞机,然后沿着一条周知的线路带你回家。这将帮助你如何更好地去实现 DDD,并且通过你所熟悉的工具和技术给出示例演示。当然,任何一个人都不可能一直呆在家里,所以我还会带领你到新的地带去冒险,这些地带你可能从来没有去过。冒险之路是险峻的,但是在正确的战术应对下,征服这些困难是可能的。在这条冒险之路上,你将学到另外的架构和模式来集成多个领域模型。你将接触到先前没有被研究过的集成方法,并且学到如何开发自治性服务。
我将向你提供一张对短途旅行和长途旅行均适用的地图,它可以帮助你更好地享受沿途风景,同时又不至于迷失途中。
对照地形,绘制飞行图
在软件开发的过程中,我们经常做的一件事便是将一种东西映射到另一种东西。我们将对象映射到数据库,映射到用户界面,或者映射到不同的应用层展现(包括作为消费方的其他系统或应用程序)。在所有这些映射中,我们很自然地希望在Evans提出的高层模式和具体实现之间存在一种映射。
即便你已经接触过 DDD,你依然有很多可以获益的地方。有时, DDD首先被看作是一套技术工具集,有人将此称为 DDD-Lite。我们可能已经对实体、服务等 DDD概念非常熟悉了,并且大胆地尝试着设计聚合,还通过资源库来管理持久化。这些模式是大家相对熟知的,使用起来很容易,我们甚至还使用了值对象。以上这些都属于战术设计模式范畴,也即更加偏向技术层面。这些模式可以很好地帮我们解决软件问题。而同时,对于战术性模式,我们依然有许多需要学习的。我将战术模式映射到实现层面。
你曾了解过战术建模之外的东西吗?你曾了解过被称为 DDD”另一半“的战略设计模式吗?如果你还没有使用过限界上下文和上下文映射图,那么你很有可能也没有使用过通用语言。
如果说 Evans在软件开发社区有一项发明,那便是通用语言。通用语言是一种团队协作模式,用于捕捉特定业务领域中的概念和术语。一个特定领域的软件模型通过不同的名词、形容词和动词来表达,这些词汇是开发团队正式使用的,而团队中应该包含一个或多个领域专家。然而,将通用语言仅限定于一些词汇则是错误的。就像自然语言反映人们的思想一样, DDD的通用语言反映了领域专家对于软件系统的思维模型。通用语言和那些战略和战术性的建模模式同等重要,在有些情况下甚至更具有持久性。
简单地讲, DDD-Lite将导致劣质的领域对象,因为通用语言、限界上下文和上下文映射图的作用太大了,你从其中获得的并不只是一套团队共用的语言。在限界上下文中用通用语言来表述一个领域模型可以增加业务价值,并且使我们确信所开发软件的正确性。即使从技术的角度,它也可以帮助我们创建更好的领域模型,这样的模型行为丰满,业务纯净,并且可以减少犯错误的可能性。因此,我将战略设计模式映射到了可理解的实际例子中。
本书对于 DDD的映射可以帮助你同时体会到战略设计和战术设计的好处。通过一些具体的例子,你将感受到这些 DDD映射的业务价值和技术展现力。
如果我们对于 DDD的所有实践都只是停留在”地面上“,那将是令人失望的。过度地拘泥于细节将使我们丧失在空中俯瞰的机会。所以,不要将自己局限在地面的细节上,要勇敢地飞翔在空中,居高临下。搭上战略设计的航班,去了解限界上下文和上下文映射图,你将获得更广阔的视野。当你从 DDD的航班中获益时,我的目的也就达到了。
各章概要
以下是各章的主要内容以及你将如何从中获益。
第1章:DDD入门
本章向你介绍 DDD的好处,并且教你如何尽可能多地去实现 DDD。你将学到当你在应对复杂的软件系统时, DDD可以为你的项目和团队带来什么。同时,你将了解到通常的 DDD替代方案以及这些方案为什么会导致问题。作为对 DDD的基础讲解,本章将教你如何在项目中开始采用 DDD,还有如何向你的领域专家和技术团队推销 DDD。在DDD的武装下,你将学会如何迎接挑战,勇往直前。
本章将介绍关于一个公司及其团队的案例研究,虽然该公司是虚构的,但是他们所面临的 DDD挑战却是真实存在的。该公司旨在开发一个新的多租户 SaaS(Software as a Service,软件即服务)软件产品。不出所料,在使用 DDD时,他们犯了一些常见的错误。不过还好,他们发现了这些错误,并解决了一些问题,因此项目还算没有偏离正轨。该团队需要开发一套基于 Scrum的项目管理软件。该案例还会在本书的后续章节中连续讲到。每一种战略和战术模式都将教给这个团队。在这个过程中,团队有误入歧途的时候,但终他们将向着成功的 DDD实践昂首阔步。
第2章:领域、子域和限界上下文
领域、子域和核心域分别是什么?限界上下文是什么,我们为什么要使用它,并且如何使用?这些问题将在这个 SaaS项目团队犯错误的时候给予解答。在他们的个 DDD项目中,他们并不了解子域、限界上下文和通用语言这些概念。事实上,他们根本不知道什么是战略设计,只是采用了战术设计来解决一些技术问题。这样他们在开始设计领域模型的时候便遇到了不少问题。幸运的是,他们及时地意识到了这些问题,项目还有挽回的余地。
本章还讲到了如何使用限界上下文对模型进行分离,这是非常重要的;同时还讲到了一些模型分离不当的反例,并且给出了有效的实现建议。在采用了这些建议之后,该团队的成员们重新创建了两个不同的限界上下文。这种合理的模型分离带来的好处是引出了第三个限界上下文–核心域,这将是本书使用的主要例子。
对于那些苦于单单从技术层面应用 DDD的人来说,本章应该能引起你的共鸣。如果你还是 DDD战略设计的外行,那么本章将为你指明方向。
第 3章:上下文映射图
上下文映射图帮助我们理解业务领域、模型间的边界,以及这些模型之间的集成方式。
上下文映射图不只是绘制系统架构图这么简单,它处理的是不同限界上下文之间的关系,以及如何在不同的模型之间映射对象。对于在复杂的业务系统中使用好限界上下文,这是至关重要的。在第 2章中,团队成员们在首次尝试限界上下文时碰到了问题。本章中,他们将学着如何利用上下文映射图来解决这些问题。这样的结果是产生了两个体面的限界上下文,这两个上下文将被另外一个负责核心域的团队所使用。
第 4章:架构
我们都知道分层架构,但它是开发 DDD软件的方式吗,也或许还存在另外的方式?在本章中,我们将讲到:六边形架构(端口和适配器)、面向服务架构、REST、CQRS、事件驱动(管道和过滤器,长时处理过程,事件源)和数据网格,其中好几种架构都将被该团队成员所采用。
第 5章:实体
在DDD的战术模式中,我们将首先讲到实体。团队成员们一开始过于强调实体的作用而忽视了值对象。受到数据库和持久化框架的影响,实体被该团队滥用了,此时他们开始讨论如何避免大范围地使用实体。
在本章中,你将看到很多优秀的实体设计例子。同时,本章还将讲到如何使用实体来表达通用语言,以及如何对实体进行测试、实现和持久化。
第 6章:值对象
早些时候,团队成员们错过了采用值对象的好机会。他们过于注重为实体创建一些单一的属性,这种方式是欠妥的,更好的方式是将这些单一的属性聚合成一个不变的整体。本章将从不同的角度讲解如何设计值对象,以及在什么时候采用值对象会优于实体。同时,本章还包含了一些其他话题,比如值对象在集成中的角色和对标准类型的建模等。然后,本章讲到了如何设计以领域为中心的测试,如何实现值对象。此外,本章还讲到了在聚合中存储值对象时,如何避免持久化机制所带来的不利影响。
第 7章:领域服务
本章将讲到,在领域模型中,什么时候应该将一个概念建模成粒度适中,并且无状态的领域服务。你将学到何时应该使用领域服务而不是实体或值对象,以及如何使用领域服务来处理业务逻辑和技术上的集成。团队成员们向我们展示了何时应该使用领域服务,以及如何设计领域服务。
第 8章:领域事件
Eric Evans并没有在他的书中正式介绍领域事件,领域事件是在他那本书出版之后才进入人们视野的。在本章中,你将学到为什么领域事件如此有用,以及使用领域事件的不同方法。领域事件甚至被用来辅助集成和自治性服务。在软件系统中,我们经常使用一些技术层面的事件机制,但本章将着重讲解领域事件与这些事件机制的区别。本章还将指导你如何设计并实现领域事件,包括一些可行的方案和对这些方案的权衡选择。然后,本章将讲到如何创建一个发布 -订阅机制;如何利用事件来集成整个企业软件中的各个订阅方;如何创建和管理事件存储;如何处理消息机制所面临的常见挑战等。
第 9章:模块
对于模型中的对象,我们应该如何将他们组织在大小适中的容器中呢?我们又如何保证不同容器中的对象之间只存在有限的耦合?另外,我们如何对这些容器进行命名以体现通用语言?除了包和命名空间之外,我们如何使用由语言和框架提供的现代模块化机制,比如 OSGi和Jigsaw?在本章中,你将看到 SaaS团队成员是如何在不同的项目中使用模块的。
第 10章:聚合
在DDD的战术模式中,聚合可能是不容易理解的了。然而,在遵循一定的经验法则的情况下,我们是能够更简单、更快地实现聚合的。在本章中你将学到:如何利用聚合在不同的小规模对象集群间创建一致性边界,从而降低模型的复杂性。由于在细枝末节上花了太多精力, SaaS团队成员们在设计聚合时总是磕磕绊绊。我们将仔细研究该团队所面临的挑战,并且分析错误的原因以及他们的应对策略。结果,团队成员们对他们的核心域有了更深层次的理解。我们将看到,在合理的事务处理和保证终一致性(Eventual Consistency)的前提下,该团队更正了他们所犯的错误,并且在一个分布式环境中设计出了更具有伸缩性和更高效的模型。
第 11章:工厂
工厂已经在 [Gamma et al.]中被大量地谈及了,为什么还要讲呢?本章并不打算重蹈覆辙,而是将重点放在”工厂应该存在于何处“这个问题上。在本章中,我们将讲到在 DDD中实现工厂的技巧。团队成员在他们的核心域中创建的工厂可以简化客户端接口,并且对模型的消费方起到保护作用,从而避免了在多租户环境中引入灾难性的 bug。
第 12章:资源库
资源库只是一个数据访问对象( Data Access Object, DAO)吗?如果不是,它们之间有什么区别呢?我们为什么应该将资源库看成是对集合的模拟而非数据库呢?在本章中,我们将讲到如何利用 ORM来实现资源库,其中有两种 ORM方案,一种采用基于网格的分布式缓存,另一种则采用 NoSQL的键值对存储。团队成员们可以采用任何一种作为他们的持久化机制。
第 13章:集成限界上下文
到现在为止,你已经了解了战略层次的上下文映射图和多种战术层次的模式。本章将讲到,在 DDD中,我们如何通过上下文映射图来集成不同的模型。在团队对核心域和其他辅助性的限界上下文进行集成时,我们将给出相应的建议和指导。
第14章:应用程序
对于每一个核心域的通用语言,我们都设计了相应的模型,并且进行了足够的测试,模型工作正常。然而,客户应该如何使用我们的模型呢?他们应该使用 DTO将数据在模型和用户界面之间传输吗?或者存在其他方案可以实现模型和展现组件间的数据传递? DDD中的应用服务和基础设施是如何工作的?对于这些问题,本章都将做出解答。
附录A:聚合与事件源: A ES
事件源是一种持久化聚合的重要技术,同时也是事件驱动架构的基础。事件源通过一系列的事件来表示聚合的所有状态。通过有序的事件重放,我们可以重新构建聚合的状态。当然,使用事件源的前提是:它能够简化对数据的持久化,并且能够捕捉到那些具有复杂行为属性的概念。
Java和开发工具
本书中的绝大多数例子都是使用 Java语言编写的。我本来可以用 C#的,但是我有意识地使用了 Java。
首先,我认为 Java社区正在抛弃好的软件设计和开发实践。现在,对于多数 Java项目而言,要在其中找到一个好的领域对象恐怕是困难的。在我看来, Scrum和敏捷被人们看成了优良设计的替代品,而其中的产品待定项(Product Backlog)被看成了设计本身。多数敏捷人士并不会过多地去思考这些待定项是否会影响到业务模型。我得说明, Scrum的本意不是要取代设计。不管有多少项目经理想将你捆绑在持续交付这条路上,我得说 Scrum并不仅仅是要取悦于那些甘特图( Gantt chart)的追随者们。然而,太多的时候,情况的确是这样的。
我认为这是个很大的问题,所以我想鼓励 Java社区重新回到领域建模中来,同时我会通过本书向大家说明,设计是可以使我们获益的。
此外,在 .NET社区中已经有很好的DDD资源了,比如Jimmy Nilsson的《领域驱动设计与模式实战》[Nilsson]。由于Jimmy的出色工作和其他人对Alt.NET的倡导,.NET社区中正掀起一阵优秀设计的开发浪潮,这是Java社区需要注意的。
其次,我意识到 C#.NET人员在理解 Java代码上并不存在什么困难。由于很多 DDD社区的人都在使用 C#.NET,而本书的早期校对人员也都是 C#程序员,但是我从来就没有收到他们的抱怨。因此,我便不用顾虑这些了。
在我写这本书时,业内正将目光从关系型数据库转向基于文档和键值对的存储方案。这是有原因的, Martin Fowler将这些存储方案称为”面向聚合存储“。这种命名是恰当的,它很好地描述了在 DDD中使用 NoSQL的好处。
但是,就我从事咨询的经验来看,很多开发者还是认定了关系型数据库和对象-关系映射。因此我想, NoSQL追随者们应该能够理解我在书中包含对象 -关系映射的章节。然而,我的确得承认,这可能会招致那些认为存在对象 -关系阻抗失配(Object-Relational Impedance)的人的鄙视。这无所谓,对此我表示接受,因为绝大多数人在他们的日常工作中都还得面对这种对象 -关系阻抗失配。
当然,在第 12章”资源库“中,我同样提供了基于文档的、键值对的和数据网格的存储方案。在多处地方,我都讨论到了 NoSQL对聚合设计的影响。 NoSQL趋势很有可能持续下去,那些对象 -关系型的开发者们应该注意了。在本书中你将看到,我能够同时理解两个阵营的观点,并且对于双方的观点我都同意。这些都是技术趋势所导致的摩擦,而这对于积极的变革是有必要的。
★“在《实现领域驱动设计》中,Vaughn不仅为DDD领域做出了贡献,还为更宽阔的企业应用架构领域写上了厚重的一笔。例如,在架构和资源库等核心章节中,Vaughn向我们展示了如何将DDD与各种架构风格和持久化技术融合在一起——包括SOA、REST、NoSQL和数据网格等——其中很多都是在Eric Evans那本DDD开山之作出版之后才出现的。另外,书中还讲到了对实体、值对象、聚合、领域服务、事件、工厂和资源库的实现,其中包括大量的例子。一言以蔽之,我认为这本书非常全面。对于那些希望提升自己技能的软件开发者来说,《实现领域驱动设计》将是一本的好书。”
——Randy Stafford,自由架构师,Oracle Coherence产品部
★“领域驱动设计是一套非常强大的思想工具,它深远地影响着软件开发团队的效率。问题在于,许多开发者在应用这套思想工具时会不时地迷失方向,他们需要更实际的指导建议。在本书中,Vaughn将理论与实践联系在了一起。除了为我们讲解那些易被误解的DDD概念之外,Vaughn还讲到了一些新的概念,比如命令/查询职责分离(CQRS)和事件源等。对于那些希望实际应用DDD的人来说,这是一本之作。”
——Udi Dahan,NServiceBus创始人
★“多年以来, DDD的开发者们都希望获得一些更实际的帮助。 Vaughn缝合了理论和实践之间的间隙,向大家提供了一套完整的 DDD实现参考。他向我们展示了如何在当前软件项目中使用DDD,并且向我们提出了大量的实际建议。 “
——Alberto Brandolini,DDD导师(由 Eric Evans和Domain Language, Inc颁发证书)
★“《实现领域驱动设计》清晰地向我们展示了 DDD的核心话题。本书的写作风格非常友好,就像一个值得信赖的导师在给你讲课一样。读完本书,你将能够应用 DDD的各个重要概念。我在阅读本书的时候,在很多章节中都做上了着重标记……我会经常地参考并推荐本书。”
——Paul Rayner,首席咨询师, DDD导师(由 Eric Evans和Domain Language, Inc颁发证书), DDD Denver创始人。
★“在我所教的 DDD课程中,很重要的一点便是如何将所有的 DDD理论付诸实践。有了本书, DDD社区便有了可供参考的资料。《实现领域驱动设计》包含了创建 DDD系统的方方面面,从具体的实现细节到高层的设计思想。这是一本了不起的 DDD参考书,同时也是 Eric Evans那本 DDD开山之作的伴侣。 “
——Patrik Fredriksson,DDD导师(由 Eric Evans和Domain Language, Inc颁发证书)
★“如果你关心软件工艺——你也应该这么做——那么领域驱动设计便是非常重要的一项技能,而《实现领域驱动设计》则向我们提供了一条迈向成功的捷径。本书详尽地讨论了 DDD的战略模式和战术模式,使开发者能够立即将理论付诸实践。今后的业务软件系统将从本书中受益匪浅。”
——Dave Muirhead,首席咨询师, Blue River Systems 集团
★“DDD既有理论,也有实践,这些都是每个开发者应该了解的,而本书则很好地弥补了理论与实践之间的差距。强烈推荐本书! “
——Rickard Oberg,Java开发者, Neo Technology公司
★“在《实现领域驱动设计》中, Vaughn采用了自顶向下的方法,首先讲到了 DDD的战略模式,比如限界上下文和上下文映射图,然后讲到了战术模式,比如实体、值对象和领域服务等。案例研究贯穿全书,要从中有所学,你需要在该案例研究上下足功夫。如果你这么做了,你便能看到将 DDD应用于复杂领域的意义所在。书中包含了大量的旁注、图标和示例代码。如果你希望使用当下常见的架构风格来创建一个 DDD系统,那么 Vaughn的这本《实现领域驱动设计》便是我所推荐的。”
——Dan Haywood,《Domain-Driven Design with Naked Objects》作者
★“本书采用了一种自顶向下的方式来讲解 DDD,这种方式将 DDD的战略模式和战术模式自然地衔接起来。在本书中, Vaughn强调了业务领域的价值,同时也给出了技术上的讨论。因此, DDD在软件开发中的角色也变得非常清晰。很多时候,我的团队,包括我本人,在应用 DDD时都会遇到这样那样的麻烦。有了《实现领域驱动设计》的指导,我们得以克服种种挑战,进而将付出立即转化为业务价值。 “
——Lev Gorodinski,首席架构师, DrillSpot.com
是的,我们将使其工作。然而,在软件开发过程中采用领域驱动设计却是困难的。即便是有能力的开发者,也很难找到实现领域驱动设计的正确方法。
起飞,着陆
在我小的时候,我的父亲学习过驾驶小型飞机。我们经常会全家出去飞行,有时会飞到另一个机场,在那里吃过午饭后再返回。当父亲时间有限而他依然想飞时,父亲便带上我一起在机场上空盘旋,起飞,着陆,再起飞,再着陆。
也会有些长途飞行,这时我们会带上一张由父亲先前绘制好的路线图。我们几个小孩便当起了领航员:将图上的标志对应着陆地上的地标,以确保我们没有跑偏航线。这是一件很有趣的事情,因为要识别远在地面上的物体是很有挑战性的。事实上,我敢肯定父亲根本不用我们领航便知道我们处于什么方位——他能看到仪表盘上的所有信息,并且他拥有仪表飞行执照。
空中的景观的确改变了我的视野。不时地,父亲和我会飞过我们乡下的房子。在几百英尺的高空中,我体会到了另一种“家”的概念,而这在之前是没有过的。当我们飞过自家的房子时,母亲和我的姐妹们便会跑到院子里向我们挥手。我知道那是她们,即便我看不清楚她们是谁。谈话肯定是不行的,连大声喊都不行,她们是听不见的。我还可以看到将我家和外面公路分开的护栏,平时我们会像走平衡木一样在护栏上面走来走去。从空中看,它们就像被细心编排过的小树枝一样。我们
家的院子很大,每每到了夏天,我都会开着割草机一排一排地修理院子里的草坪。而在空中时,我只能看到一片绿色,小草的叶子肯定是看不清楚的。
我喜欢在空中的时刻,直到现在我还不时回想起这些时刻,好像那个降落飞
机的黄昏就发生在不久以前一样。虽然如此,在地面上的感觉依然是无法取代的,
因为它给我一种脚踏实地的感觉。
着陆于领域驱动设计
一开始接触领域驱动设计( DDD)就像一个小孩之于飞行一样。天空中的景色是令人惊叹的,但有时我们却因为过于陌生而搞不明白它们到底是什么。要从甲地到乙地显得如此的遥远。然而, DDD的“成年人 ”们却总知道他们所处的方位,因为他们在很早之前便绘制好了路线图,并且能够完全按照仪表进行相应的操作。而还有很多人找不到“在地面上”的感觉,此时我们需要的是“稳定着陆”的能力,然后找到一张地图给我们指引方向。
Eric Evans的《领域驱动设计:软件核心复杂性应对之道》是一本经得住时间考验的经典之作。我坚定地相信,在接下来的几十年里,本书依然会是开发者的实用指导。和其他模式一样,该书为我们建立起了一种高屋建瓴式的宽阔视野。然而,对于如何实现 DDD,我们可能将面对更多的挑战。通常来说,我们更渴望看到一些具体的例子。
我的目标之一便是帮助你来一个“软着陆”,保全飞机,然后沿着一条周知的线路带你回家。这将帮助你如何更好地去实现 DDD,并且通过你所熟悉的工具和技术给出示例演示。当然,任何一个人都不可能一直呆在家里,所以我还会带领你到新的地带去冒险,这些地带你可能从来没有去过。冒险之路是险峻的,但是在正确的战术应对下,征服这些困难是可能的。在这条冒险之路上,你将学到另外的架构和模式来集成多个领域模型。你将接触到先前没有被研究过的集成方法,并且学到如何开发自治性服务。
我将向你提供一张对短途旅行和长途旅行均适用的地图,它可以帮助你更好地享受沿途风景,同时又不至于迷失途中。
对照地形,绘制飞行图
在软件开发的过程中,我们经常做的一件事便是将一种东西映射到另一种东西。我们将对象映射到数据库,映射到用户界面,或者映射到不同的应用层展现(包括作为消费方的其他系统或应用程序)。在所有这些映射中,我们很自然地希望在Evans提出的高层模式和具体实现之间存在一种映射。
即便你已经接触过 DDD,你依然有很多可以获益的地方。有时, DDD首先被看作是一套技术工具集,有人将此称为 DDD-Lite。我们可能已经对实体、服务等 DDD概念非常熟悉了,并且大胆地尝试着设计聚合,还通过资源库来管理持久化。这些模式是大家相对熟知的,使用起来很容易,我们甚至还使用了值对象。以上这些都属于战术设计模式范畴,也即更加偏向技术层面。这些模式可以很好地帮我们解决软件问题。而同时,对于战术性模式,我们依然有许多需要学习的。我将战术模式映射到实现层面。
你曾了解过战术建模之外的东西吗?你曾了解过被称为 DDD“另一半”的战略设计模式吗?如果你还没有使用过限界上下文和上下文映射图,那么你很有可能也没有使用过通用语言。
如果说 Evans在软件开发社区有一项发明,那便是通用语言。通用语言是一种团队协作模式,用于捕捉特定业务领域中的概念和术语。一个特定领域的软件模型通过不同的名词、形容词和动词来表达,这些词汇是开发团队正式使用的,而团队中应该包含一个或多个领域专家。然而,将通用语言仅限定于一些词汇则是错误的。就像自然语言反映人们的思想一样, DDD的通用语言反映了领域专家对于软件系统的思维模型。通用语言和那些战略和战术性的建模模式同等重要,在有些情况下甚至更具有持久性。
简单地讲, DDD-Lite将导致劣质的领域对象,因为通用语言、限界上下文和上下文映射图的作用太大了,你从其中获得的并不只是一套团队共用的语言。在限界上下文中用通用语言来表述一个领域模型可以增加业务价值,并且使我们确信所开发软件的正确性。即使从技术的角度,它也可以帮助我们创建更好的领域模型,这样的模型行为丰满,业务纯净,并且可以减少犯错误的可能性。因此,我将战略设计模式映射到了可理解的实际例子中。
本书对于 DDD的映射可以帮助你同时体会到战略设计和战术设计的好处。通过一些具体的例子,你将感受到这些 DDD映射的业务价值和技术展现力。
如果我们对于 DDD的所有实践都只是停留在“地面上”,那将是令人失望的。过度地拘泥于细节将使我们丧失在空中俯瞰的机会。所以,不要将自己局限在地面的细节上,要勇敢地飞翔在空中,居高临下。搭上战略设计的航班,去了解限界上下文和上下文映射图,你将获得更广阔的视野。当你从 DDD的航班中获益时,我的目的也就达到了。
各章概要
以下是各章的主要内容以及你将如何从中获益。
第1章:DDD入门
本章向你介绍 DDD的好处,并且教你如何尽可能多地去实现 DDD。你将学到当你在应对复杂的软件系统时, DDD可以为你的项目和团队带来什么。同时,你将了解到通常的 DDD替代方案以及这些方案为什么会导致问题。作为对 DDD的基础讲解,本章将教你如何在项目中开始采用 DDD,还有如何向你的领域专家和技术团队推销 DDD。在DDD的武装下,你将学会如何迎接挑战,勇往直前。
本章将介绍关于一个公司及其团队的案例研究,虽然该公司是虚构的,但是他们所面临的 DDD挑战却是真实存在的。该公司旨在开发一个新的多租户 SaaS(Software as a Service,软件即服务)软件产品。不出所料,在使用 DDD时,他们犯了一些常见的错误。不过还好,他们发现了这些错误,并解决了一些问题,因此项目还算没有偏离正轨。该团队需要开发一套基于 Scrum的项目管理软件。该案例还会在本书的后续章节中连续讲到。每一种战略和战术模式都将教给这个团队。在这个过程中,团队有误入歧途的时候,但终他们将向着成功的 DDD实践昂首阔步。
第2章:领域、子域和限界上下文
领域、子域和核心域分别是什么?限界上下文是什么,我们为什么要使用它,并且如何使用?这些问题将在这个 SaaS项目团队犯错误的时候给予解答。在他们的个 DDD项目中,他们并不了解子域、限界上下文和通用语言这些概念。事实上,他们根本不知道什么是战略设计,只是采用了战术设计来解决一些技术问题。这样他们在开始设计领域模型的时候便遇到了不少问题。幸运的是,他们及时地意识到了这些问题,项目还有挽回的余地。
本章还讲到了如何使用限界上下文对模型进行分离,这是非常重要的;同时还讲到了一些模型分离不当的反例,并且给出了有效的实现建议。在采用了这些建议之后,该团队的成员们重新创建了两个不同的限界上下文。这种合理的模型分离带来的好处是引出了第三个限界上下文——核心域,这将是本书使用的主要例子。
对于那些苦于单单从技术层面应用 DDD的人来说,本章应该能引起你的共鸣。如果你还是 DDD战略设计的外行,那么本章将为你指明方向。
第 3章:上下文映射图
上下文映射图帮助我们理解业务领域、模型间的边界,以及这些模型之间的集成方式。
上下文映射图不只是绘制系统架构图这么简单,它处理的是不同限界上下文之间的关系,以及如何在不同的模型之间映射对象。对于在复杂的业务系统中使用好限界上下文,这是至关重要的。在第 2章中,团队成员们在首次尝试限界上下文时碰到了问题。本章中,他们将学着如何利用上下文映射图来解决这些问题。这样的结果是产生了两个体面的限界上下文,这两个上下文将被另外一个负责核心域的团队所使用。
第 4章:架构
我们都知道分层架构,但它是开发 DDD软件的方式吗,也或许还存在另外的方式?在本章中,我们将讲到:六边形架构(端口和适配器)、面向服务架构、REST、CQRS、事件驱动(管道和过滤器,长时处理过程,事件源)和数据网格,其中好几种架构都将被该团队成员所采用。
第 5章:实体
在DDD的战术模式中,我们将首先讲到实体。团队成员们一开始过于强调实体的作用而忽视了值对象。受到数据库和持久化框架的影响,实体被该团队滥用了,此时他们开始讨论如何避免大范围地使用实体。
在本章中,你将看到很多优秀的实体设计例子。同时,本章还将讲到如何使用实体来表达通用语言,以及如何对实体进行测试、实现和持久化。
第 6章:值对象
早些时候,团队成员们错过了采用值对象的好机会。他们过于注重为实体创建一些单一的属性,这种方式是欠妥的,更好的方式是将这些单一的属性聚合成一个不变的整体。本章将从不同的角度讲解如何设计值对象,以及在什么时候采用值对象会优于实体。同时,本章还包含了一些其他话题,比如值对象在集成中的角色和对标准类型的建模等。然后,本章讲到了如何设计以领域为中心的测试,如何实现值对象。此外,本章还讲到了在聚合中存储值对象时,如何避免持久化机制所带来的不利影响。
第 7章:领域服务
本章将讲到,在领域模型中,什么时候应该将一个概念建模成粒度适中,并且无状态的领域服务。你将学到何时应该使用领域服务而不是实体或值对象,以及如何使用领域服务来处理业务逻辑和技术上的集成。团队成员们向我们展示了何时应该使用领域服务,以及如何设计领域服务。
第 8章:领域事件
Eric Evans并没有在他的书中正式介绍领域事件,领域事件是在他那本书出版之后才进入人们视野的。在本章中,你将学到为什么领域事件如此有用,以及使用领域事件的不同方法。领域事件甚至被用来辅助集成和自治性服务。在软件系统中,我们经常使用一些技术层面的事件机制,但本章将着重讲解领域事件与这些事件机制的区别。本章还将指导你如何设计并实现领域事件,包括一些可行的方案和对这些方案的权衡选择。然后,本章将讲到如何创建一个发布 -订阅机制;如何利用事件来集成整个企业软件中的各个订阅方;如何创建和管理事件存储;如何处理消息机制所面临的常见挑战等。
第 9章:模块
对于模型中的对象,我们应该如何将他们组织在大小适中的容器中呢?我们又如何保证不同容器中的对象之间只存在有限的耦合?另外,我们如何对这些容器进行命名以体现通用语言?除了包和命名空间之外,我们如何使用由语言和框架提供的现代模块化机制,比如 OSGi和Jigsaw?在本章中,你将看到 SaaS团队成员是如何在不同的项目中使用模块的。
第 10章:聚合
在DDD的战术模式中,聚合可能是不容易理解的了。然而,在遵循一定的经验法则的情况下,我们是能够更简单、更快地实现聚合的。在本章中你将学到:如何利用聚合在不同的小规模对象集群间创建一致性边界,从而降低模型的复杂性。由于在细枝末节上花了太多精力, SaaS团队成员们在设计聚合时总是磕磕绊绊。我们将仔细研究该团队所面临的挑战,并且分析错误的原因以及他们的应对策略。结果,团队成员们对他们的核心域有了更深层次的理解。我们将看到,在合理的事务处理和保证终一致性(Eventual Consistency)的前提下,该团队更正了他们所犯的错误,并且在一个分布式环境中设计出了更具有伸缩性和更高效的模型。
第 11章:工厂
工厂已经在 [Gamma et al.]中被大量地谈及了,为什么还要讲呢?本章并不打算重蹈覆辙,而是将重点放在“工厂应该存在于何处”这个问题上。在本章中,我们将讲到在 DDD中实现工厂的技巧。团队成员在他们的核心域中创建的工厂可以简化客户端接口,并且对模型的消费方起到保护作用,从而避免了在多租户环境中引入灾难性的 bug。
第 12章:资源库
资源库只是一个数据访问对象( Data Access Object, DAO)吗?如果不是,它们之间有什么区别呢?我们为什么应该将资源库看成是对集合的模拟而非数据库呢?在本章中,我们将讲到如何利用 ORM来实现资源库,其中有两种 ORM方案,一种采用基于网格的分布式缓存,另一种则采用 NoSQL的键值对存储。团队成员们可以采用任何一种作为他们的持久化机制。
第 13章:集成限界上下文
到现在为止,你已经了解了战略层次的上下文映射图和多种战术层次的模式。本章将讲到,在 DDD中,我们如何通过上下文映射图来集成不同的模型。在团队对核心域和其他辅助性的限界上下文进行集成时,我们将给出相应的建议和指导。
第14章:应用程序
对于每一个核心域的通用语言,我们都设计了相应的模型,并且进行了足够的测试,模型工作正常。然而,客户应该如何使用我们的模型呢?他们应该使用 DTO将数据在模型和用户界面之间传输吗?或者存在其他方案可以实现模型和展现组件间的数据传递? DDD中的应用服务和基础设施是如何工作的?对于这些问题,本章都将做出解答。
附录A:聚合与事件源: A ES
事件源是一种持久化聚合的重要技术,同时也是事件驱动架构的基础。事件源通过一系列的事件来表示聚合的所有状态。通过有序的事件重放,我们可以重新构建聚合的状态。当然,使用事件源的前提是:它能够简化对数据的持久化,并且能够捕捉到那些具有复杂行为属性的概念。
Java和开发工具
本书中的绝大多数例子都是使用 Java语言编写的。我本来可以用 C#的,但是我有意识地使用了 Java。
首先,我认为 Java社区正在抛弃好的软件设计和开发实践。现在,对于多数 Java项目而言,要在其中找到一个好的领域对象恐怕是困难的。在我看来, Scrum和敏捷被人们看成了优良设计的替代品,而其中的产品待定项(Product Backlog)被看成了设计本身。多数敏捷人士并不会过多地去思考这些待定项是否会影响到业务模型。我得说明, Scrum的本意不是要取代设计。不管有多少项目经理想将你捆绑在持续交付这条路上,我得说 Scrum并不仅仅是要取悦于那些甘特图( Gantt chart)的追随者们。然而,太多的时候,情况的确是这样的。
我认为这是个很大的问题,所以我想鼓励 Java社区重新回到领域建模中来,同时我会通过本书向大家说明,设计是可以使我们获益的。
此外,在 .NET社区中已经有很好的DDD资源了,比如Jimmy Nilsson的《领域驱动设计与模式实战》[Nilsson]。由于Jimmy的出色工作和其他人对Alt.NET的倡导,.NET社区中正掀起一阵优秀设计的开发浪潮,这是Java社区需要注意的。
其次,我意识到 C#.NET人员在理解 Java代码上并不存在什么困难。由于很多 DDD社区的人都在使用 C#.NET,而本书的早期校对人员也都是 C#程序员,但是我从来就没有收到他们的抱怨。因此,我便不用顾虑这些了。
在我写这本书时,业内正将目光从关系型数据库转向基于文档和键值对的存储方案。这是有原因的, Martin Fowler将这些存储方案称为“面向聚合存储”。这种命名是恰当的,它很好地描述了在 DDD中使用 NoSQL的好处。
但是,就我从事咨询的经验来看,很多开发者还是认定了关系型数据库和对象-关系映射。因此我想, NoSQL追随者们应该能够理解我在书中包含对象 -关系映射的章节。然而,我的确得承认,这可能会招致那些认为存在对象 -关系阻抗失配(Object-Relational Impedance)的人的鄙视。这无所谓,对此我表示接受,因为绝大多数人在他们的日常工作中都还得面对这种对象 -关系阻抗失配。
当然,在第 12章“资源库”中,我同样提供了基于文档的、键值对的和数据网格的存储方案。在多处地方,我都讨论到了 NoSQL对聚合设计的影响。 NoSQL趋势很有可能持续下去,那些对象 -关系型的开发者们应该注意了。在本书中你将看到,我能够同时理解两个阵营的观点,并且对于双方的观点我都同意。这些都是技术趋势所导致的摩擦,而这对于积极的变革是有必要的。
评论
还没有评论。