资源描述
领 域 驱 动 设 计与 模 型 驱 动 开 发 钟 玮 军2015年 3月 致 谢 : 此 培 训 材 料 借 鉴 了 来 自 参 考 文 献 以 及 互 联 网 的 大 量 资 料 , 部 分 资 料 的 参考 来 源 未 能 尽 数 列 举 , 谨 在 此 对 那 些 在 网 络 中 无 私 分 享 自 己 知 识 的 人 表 达 我的 衷 心 感 谢 ! 培训内容领 域 驱 动 设 计 简 介领 域 通 用 语 言领 域 驱 动 设 计 的 构 造 块领 域 驱 动 设 计 编 程 实 践CQRS架 构模 型 驱 动 开 发 领域驱动设计思想的发展 2002年Martin Fower在其出版企业应用架构模式中,归纳总结了40多种企业应用架构的设计模式。其中所提到的多种设计模式和概念,如事务脚本、活动记录和领域模型等,对业界产生了深远的影响。 2004年著名建模专家Eric Evans发表了他最具影响力的著名书籍:Domain-Driven Design Tackling Complexity in the Heart of Software(中文译名:领域驱动设计软件核心复杂性应对之道),书中提出了“领域驱动设计(简称DDD)”的概念。 2010年Greg Young在“CQRS, Task Based UIs, Event Sourcing agh! ”一文中对Betrand Meyer的CQS模式进行改造,提出CQRS模式。 此后Jimmy Nilsson的Applying Domain-Driven Design and Patterns、Abel Avram和Floyd Marinescu合作的Domain-Driven Design Quickly、Dan Haywood的Domain-Driven Design Using Naked Objects、以及Vaughn Vernon的Implementing Domain-Driven Design等书籍的出版,丰富了领域驱动设计的实践和指导。 领域驱动设计是什么 领域驱动设计事实上针对是OOAD的一个扩展和延伸,DDD基于面向对象分析与设计技术,对技术框架进行了分层规划,同时对每个类进行了策略和类型的划分。n Its a set of proven modeling techniques especially targeted to complex applications.n Its a set of principles and practices supporting the development process.n Its a set of patterns that support a clean and coherent view of the domain model. n Its a set of pragmatic strategies allowing applications to scale in size and complexity maintaining their integrity. 领域驱动设计的特性 成 熟 、 清 晰 的 分 层 架 构 领 域 对 象 与 现 实 世 界 的 业 务 映 射 明 确 的 职 责 划 分分 层 架 构 领 域 对 象 是 核 心 领 域 对 象 复 用 : 完 整 的 业 务 对 象 描 述 设 计 复 用 : 设 计 基 于 领 域 对 象 而 非 数 据 库复 用 具 备 复 杂 业 务 逻 辑 的 软 件 开 发 对 设 计 和 开 发 人 员 要 求 较 高 不 适 用 普 通 CRUD的 业 务 软 件 的 维 护 性 和 扩 展 性 良 好 (Testable)使 用 场 景 领域驱动设计分层规划(一) 领域驱动设计分层规划 用 户 界 面 /展 现 层 负 责 向 用 户 展 现 信 息 以 及 解 释 用 户 命 令 。展 示 层 的 组 件 实 现 用 户 与 应 用 交 互 的 功 能 。一 般 建 议 用 MVC, MVP或 者 MVVM模 式来 分 隔 这 些 组 件 为 子 层应 用 层 很 薄 的 一 层 , 用 来 协 调 应 用 的 活 动 , 实 现协 调 应 用 的 “ 通 道 ” , 例 如 事 务 、 执 行 单位 操 作 、 调 用 应 用 程 序 的 任 务 。 它 不 包 含业 务 逻 辑 。 它 不 保 留 业 务 对 象 的 状 态 , 但它 保 有 应 用 任 务 的 进 度 状 态 。类 似 于 Faade模 式 , 调 用 领 域 层 和 基 础 设 施 层 来 完 成 应 用 的 用 例 。领 域 层 本 层 包 含 关 于 领 域 的 信 息 。 这 是 业 务 软 件的 核 心 所 在 。 在 这 里 保 留 业 务 对 象 的 状 态 ,对 业 务 对 象 和 它 们 状 态 的 持 久 化 被 委 托 给了 基 础 设 施 层 。基 础 设 施层 本 层 作 为 其 他 层 的 支 撑 库 存 在 。 它 提 供 了层 间 的 通 信 , 实 现 对 业 务 对 象 的 持 久 化 ,包 含 对 用 户 界 面 层 的 支 撑 库 等 作 用 。 领域驱动设计分层规划(二) 领域驱动设计是对传统N层架构模式的继承和发展 领域驱动设计分层规划(三) 领域驱动设计是对传统N层架构模式的继承和发展 Core J2EE Patterns 例 : J2EE参 考 分 层 架 构 传 统 J2EE或Spring+Hibernate等 事 务 性编 程 模 型 只 关 心 数 据 , 这 些数 据 对 象 除 了 简 单sette/getter方 法 外 , 没 有 任何 业 务 方 法 , 被 比 喻 成 “ 失血 模 型 ” 。 领域驱动设计分层规划(四) 分布式领域驱动设计 领域驱动设计分层规划(五) 分布式领域驱动设计与DotNET技术架构体系之间的关系映射 面向对象分析与设计技术 面向过程vs.面向对象 事 务 脚 本 模 式 把 业 务 逻 辑 组 织 成 单 个 过 程 , 在 过 程 中 直 接 调 用 数 据 库 , 业 务 逻 辑 在 服 务 (Service)层 处 理 。 事 务 脚 本 模 式 的 特 点 是 简 单 容 易 理 解 , 面 向 过 程 设 计 。 对 于 少 量 逻 辑 的 业 务 应 用 来 说 , 事 务 脚 本模 式 简 单 自 然 , 性 能 良 好 , 容 易 理 解 , 而 且 一 个 事 务 的 处 理 不 会 影 响 其 他 事 务 。 不 过 缺 点 也 很 明 显 , 对 于 复 杂 的 业 务 逻 辑 处 理 力 不 从 心 , 难 以 保 持 良 好 的 设 计 , 事 务 之 间 的 冗 余代 码 不 断 增 多 , 通 过 复 制 粘 贴 方 式 进 行 复 用 。 可 维 护 性 和 扩 展 性 变 差 。 对类的策略和类型的划分 对 类 进 行 StereoType(“ 构 造 型 ” )划 分 的 好 处 在 于 : ( 1) 指 导 设 计 ( 2) 帮 助 命 令 对 象 ( 3) 辅 助 理 解 按照策略和类型对类进行划分 六边形架构 以领域模型为核心的六边形架构 领域驱动设计中的设计模式 有助于获得柔性设计的设计模式 每 个 元 素 的 名 称 都 提 供了 一 次 揭 示 设 计 意 图 的机 会 。 站 在 客 户 开 发 人员 的 角 度 上 来 思 考 它 。 人 们 为 了 使 所 有 类 和 操 作 都 具 有 相 似 的 规 模而 寻 找 一 种 一 致 的 力 度 。 粒 度 的 大 小 并 不 是唯 一 要 考 虑 的 问 题 , 我 们 还 要 考 虑 粒 度 在 哪种 场 合 下 使 用 。随 着 代 码 重 构 不 断 适 合 新 理 解 的 概 念 或 需 求 ,概 念 轮 廓 也 就 逐 渐 形 成 了 。 搞 内 聚 低 耦 合 原则 既 适 用 于 代 码 , 也 适 用 于 概 念 。 领 域 驱 动 设 计 软 件 核 心 复 杂 性 应 对 之 道 第 10章 任 何 对 未 来 操 作 产 生 影 响 的 系 统状 态 的 改 变 都 可 以 成 为 副 作 用 。把 命 令 和 查 询 严 格 地 放 到 不 同 操作 中 ; 创 建 并 返 回 Value Object。允 许 我 们 安 全 地 对 多 个 操 作 进 行组 合 。 使 用 断 言 把 副 作 用 明确 表 示 出 来 , 使 它 们更 易 于 处 理 。寻 找 在 概 念 上 内 聚 的模 型 , 更 易 推 出 预 期ASSERTION, 从 而加 快 学 习 过 程 并 避 免代 码 矛 盾 。 尽 一 切 可 能 保 持 低 耦 合 。 把 所 有 无 关 概 念 提 取 到对 象 之 外 , 类 就 变 成 完 全 孤 立 的 了 , 使 得 我 们 可以 单 独 地 研 究 和 理 解 它 。 每 个 孤 立 类 都 极 大 减 轻了 因 理 解 Module而 带 来 的 负 担 。操 作 闭 合 : 在 适 当 的 情 况 下 , 在定 义 操 作 时 让 它 的 返 回 类 型 与 其参 数 相 同 。 闭 合 操 作 提 供 了 一 个高 层 接 口 , 同 时 又 不 会 引 入 对 其他 概 念 的 任 何 依 赖 性 。 培训内容领 域 驱 动 设 计 简 介领 域 通 用 语 言领 域 驱 动 设 计 的 构 造 块领 域 驱 动 设 计 编 程 实 践CQRS架 构模 型 驱 动 开 发 使用通用语言的重要性 Talking different languages makes projects fail.n Programmers speak using technical jargon (design patterns, acronyms, geeky in-jokes)n Domain experts use terminology specific to their field of expertise n Computers speak programming languagesn 大 家 必 须 妥 协 领域驱动设计的关键点 关注核心领域(Core Domain) 领域专家和软件从业者共同开发模型 在一个明确的限界上下文(Bounded Context)中使用领域通用语言(ubiquitous language) 通用语言(一) 通用语言(UBIQUITOUS LANGUAGE)是团队共享的语言。领域专家和开发者使用相同的通用语言进行交流。事实上,团队中每个人都使用相同的通用语言。不管你在团队中的角色如何,只要你是团队的一员,你都将使用通用语言。n 通用语言是团队自己创建的公用语言。团队中同时包含领域专家和软件开发人员。n 通用语言更多地是关于业务本身如何思考和运作的,领域专家对通用语言有很大影响。不同领域专家会在概念和术语上产生分歧,甚至也会犯错,当领域专家和开发者一起创建领域模型的时候,他们有时会达成一致,有时会做一些妥协,但最终目的都是为了创造最适合项目的通用语言。团队成员们妥协的绝对不应是通用语 言的质量,而是概念、术语和含义。最初的一致并不表示始终一致,通用语言也会随着时间推移而不断演化改变。n 领域驱动设计的一个核心思想就是使用基于模型的共同语言。因 为 模 型 是 软 件 满 足 领 域 的 共 同 点 , 它 很 适 合作 为 这 种 通 用 语 言 的 构 造 基 础 。 使 用 模 型 作 为 语 言 的 核 心 骨 架 , 要 求 团 队 在 进 行 所 有 的 交 流 都 是 使 用 一 致 的语 言 , 在 代 码 中 也 是 这 样 。 在 共 享 知 识 和 推 敲 模 型 时 , 团 队 会 使 用 语 言 、 文 字 和 图 形 。 这 儿 需 要 确 保 团 队 使用 的 语 言 在 所 有 的 交 流 形 式 中 看 上 去 都 是 一 致 的 , 这 种 语 言 被 称 为 “ 通 用 语 言 ( Ubiquitous Language) ” 。n 通用语言的词汇表包括类名称和主要操作。语 言 中 包 含 术 语 , 有 些 术 语 用 来 讨 论 模 型 中 已 经 明 确 的 规 则 , 还有 一 些 术 语 则 来 自 施 加 于 模 型 上 的 高 级 组 织 原 则 。 最 后 , 团 队 一 致 应 用 于 领 域 模 型 的 模 式 名 称 使 这 种 语 言 更为 丰 富 。 模 型 之 间 的 关 系 成 为 所 有 语 言 都 具 有 的 组 合 规 则 , 词 和 短 语 的 意 义 反 映 了 模 型 的 语 义 。 通用语言(二) 在应用通用语言时,应注意:n 将模型作为语言的中心。确保团队在所有交流活动和代码中坚持使用这种语言。在画图、写东西特别是讲话时也要使用这种语言。n 通过尝试不同的表示方法(它们反映了不同模型)来消除难点。然后重构代码,并对类、方法和模块重新命名,以便与新模型相一致。解决交谈中的术语混淆问题,就像我们对普通词汇形成一个公认的理解一样。n 要认识到UBIQUITOUS LANGUAGE中的更改就是对模型的更改。n 领域专家应该避免使用拗口或无法表达领域理解的术语或结构,开发人员应该密切监视那些将会妨碍设计的有歧义和不一致的地方 有了通用语言,模型就不仅仅是一个设计工作了。它成为开发人员和领域专家共同完成的每项工作中的不可或缺的部分。语言以动态形式传递知识。使用这种语言进行讨论能够更清楚地表达图和代码背后的真实含义。 通用语言是那些不以代码形式出现的设计方面的主要载体,这些方面包括把整个系统组织在一起的比例结构、定义了不同系统和模型之间关系的Bounded Context,以及在模型和设计中使用的其他模式。 通用语言的应用 通用语言贯穿于项目的各个环节n User Storiesn Project Meetingsn Team Emailsn Instant Messagesn Schedule Plann Software Documents 在限界上下文中,保持语言的一致性(如口语、图形(如UML图等)、文字、代码等)。 通用语言的应用示例(一) User StoriesNOWhen User logs on with valid credentials, an empty panel is displayed.YESWhen Player logs on with valid credentials, an empty board game is displayed. (from a Tic Tac Toe Game software example) 通用语言的应用示例(二) Code ExampleNO. Integer i = new Integer();. String char1 = new String();. public class GameDAO() . catch (Exception e)YES . String realMeaningOfMyString = new String();. public class ScoreDataLoader() . catch (Exception NotLoggedInException) NO. Ambiguities. Inconsistencies. Synonyms. AbbreviationsYES. Clarity. Precision. Reuse. Full Names package tictactoe.client.userInterface;/* Add the string O or X to a cell in the grid.*/public class ShowCellGridpublic static void displayUser (Grid grid, Cell cell) if (!Initialization.flag String mk= showString(Initialization.gameStatus .getCurrentUser().getUserString(); grid.setHTML(cell.getRowIndex(), cell.getCellIndex(), mk); Initialization.gameStatus.getStatus()cell.getRowIndex()cell .getCellIndex() = Initialization.gameStatus .getCurrentUser(); GameEnd.checkEnd(Initialization.gameStatus, cell.getRowIndex(), cell.getCellIndex(); (.) A class BEFORE and AFTER Ubiquitous Languagepackage tictactoe.client.userInterface;/* Performs a move in the game.*/public class PlayerMove /* When the player clicks in a cell, the game draws an O or a X on the * game grid depending on which players turn it is.*/public static void makeMove (GameGrid gameGrid, Cell cell) if (!GameInitialization.waitingMoveFlag String marker = showPlayerIcon(GameInitialization.currentGameStatus .getCurrentPlayer().getPlayerIcon(); gameGrid.setHTML(cell.getRowIndex(), cell.getCellIndex(), marker); GameInitialization.currentGameStatus.getGameMoves()cell.getRowIndex()cell.getCellIndex() = GameInitialization.currentGameStatus.getCurrentPlayer(); CheckWinner.checkForWinner(GameInitialization.currentGameStatus, cell.getRowIndex(), cell.getCellIndex(); (.)(Excerpted from a Tic Tac Toe Game source code) Which one would a Stakeholder better understand?Player Move Performs a move in the game. Make Move When the player clicks in a cell, the game draws an O or a X on the game grid depending on which players turn it is. Is Cell Empty The Player can select a cell only if it wasnt already selected.Show Cell Grid Add the String O or X to a cell in the grid. Display User Is Empty (Excerpted from a Tic Tac Toe Game source code) 模型的统一 模型的内部一致性又叫做“统一”,这样每个术语都不会有模棱两可的意义,也不会有规则冲突。除非模型在逻辑上是一致的,否则它就没有意义。 识别限界上下文中的不一致:重复的概念和假同源n 重复的概念是指两个模型元素(以及伴随的实现)实际上表示同一个概念。每当这个概念的信息发生改变时,都必须要更新两个地方。每次由于新的知识导致一个对象被修改时,也必须重新分析和修改另一个对象。如果不进行实际的重新分析,结果就会出现同一个概念的两个版本,它们遵守不同的规则,甚至不同的数据。更重要的是,团队成员必须学习同一操作的两种方法,以及保持这两种方法同步的各种方式。 n 假同源是指使用相同术语(或已实现的对象)的两个人认为他们是在谈论同一件事情,但实际上并不是这样。但是,当两个定义都与同一个领域方面相关,而只是在概念上稍有区别时,这种冲突更难以发现。假同源会导致开发团队互相干扰对方的代码,也可能导致数据库中含有奇怪的矛盾,还会引起团队沟通的混淆。 注意用词词汇n 注意正确用词,不要歪曲词义n 开发人员经常习惯于使用增/删/改/查(CRUD)此类动词词汇,也许有时候它们也确实属于通用语言,但大多数情况下,它们并不能正确反映业务,用词上混淆了业务概念。 模型的分裂 在理想的世界中,我们可以有一种把整个企业领域包含进来的单一模型;这个模型将是统一的,没有任何相互矛盾或相互重叠的术语定义;每个有关领域的逻辑声明都将是一致的。但大型系统开发并不是这样理想。 大型系统领域模型的完全统一是不可行的,也不是一种经济有效的做法。我们可以采用限界上下文(Bounded Context)定义每个模型的应用范围,采用上下文映射(Context Map)给出项目上下文以及它们之间关系的总体视图。 n 任何一个大型项目都会存在多个模型。而当基于不同模型的代码被组合到一起后,软件就会出现bug、变得不可靠和难以理解。团队成员之间的沟通变得混乱。人们往往弄不清楚一个模型不应该在哪个上下文中使用。n 明确地定义模型所应用的上下文。根据团队的组织、软件系统的各个部分的用法以及物理表现(代码和数据库模式等)来设置模型的边界。在这些边界中严格保持模型的一致性,而不要受到边界之外问题的干扰和混淆。在Context中,要保证模型在逻辑上统一,而不用考虑它是不是适用于边界之外的情况。在其他Context中,会使用其他的模型,这些模型具有不同的术语、概念、规则和UBIQUITOUS LANGUAGE的技术行话。 n 定义Bounded Context:视察项目的现状,而不是它的理想状态。 领域、子域和限界上下文 核心域、支撑域和通用域 A Core Domain is a part of the business Domain that is of primary importance to the success of the organization. It is of utmost importance to the ongoing success of the business. If a domain models some aspect of the business that is essential, yet not Core, it is a Supporting Subdomain. if a domain captures nothing special to the business, yet is required for the overall business solution, it is a Generic Subdomain. Focus on the core domain 战术建模与战略建模 领域驱动设计的综合应用 共享内核(Shared Kernel) 当不同团队开发一些紧密相关的应用程序时,如果团队之间不进行协调,即使短时间内能够取得快速进展,他们开发出的产品也可能互相不适合,最后可能不得不在转换层上花费大量时间,而且得到的产品也五花八门。n 从 领 域 模 型 中 选 出 两 个 团 队 都 同 意 共 享 的 一 个 子 集 。 当 然 , 除 了 模 型 的 这 个 子 集 以 外 , 这 还 包 括 与该 模 型 部 分 相 关 的 代 码 子 集 , 或 数 据 库 设 计 的 子 集 。 这 部 分 明 确 共 享 的 内 容 具 有 特 殊 的 状 态 , 而 且一 个 团 队 在 没 与 另 一 个 团 队 商 量 的 情 况 下 不 应 擅 自 更 改 它 。 n 功 能 系 统 要 经 常 进 行 集 成 , 但 集 成 的 频 率 应 该 比 团 队 中 Continuous Integration的 频 率 低 一 些 。 在 进行 这 些 集 成 的 时 候 , 两 个 团 队 都 要 运 行 测 试 。n Shared Kernel通 常 是 Core Domain, 或 是 一 组 Generic Subdomain( 通 用 子 领 域 ) , 也 可 能 二 者 兼有 。 企业架构方法与领域驱动设计3. 架 构 内 容 框 架 4. 企 业 连 续 系 列1. 架 构 开 发方 法2. 架 构 开 发 指 引 和 技 术 5. 参 考 模 型6. 架 构 能 力 框 架 两者都强调Business和IT的高度统一,很多企业架构方法对于领域驱动设计“战略设计”的具体实施办法具有详实的指导意义。如TOGAF V9构件: eTOM业务建模Level 0 ProcessesLevel 1 Processes Level 2 Processes业 务 流 程 解 耦 /分 解 eTOM业务建模BSS业 务 流 程 框 架 领 域 解 决 特 定 问 题 eTOM信息数据模型eTOM 0级视图 SID 1级视图ABE:Aggregate Business Entity,ABE是 SID中一组定义良好的实体,具有高内聚、低耦合的特征。 共 享 内 核 eTOM信息数据模型 参考读物 领域驱动设计软件核心复杂性应对之道及实现领域驱动设计中的相关章节 软件方法-业务建模和需求第三章“业务建模”中的相关内容 参考模型范例:n TMForum的 eTOM模 型 : http:/ 培训内容领 域 驱 动 设 计 简 介领 域 通 用 语 言领 域 驱 动 设 计 的 构 造 块领 域 驱 动 设 计 编 程 实 践CQRS架 构模 型 驱 动 开 发 领域驱动设计的构造块 Entity(实体) 实体是一个具有唯一身份标识的对象,并且可以在相当长的一段时间内持续地变化。我们可以对实体做多次修改,故一个实体对象可能和它先前的对象大不相同,但是由于它们拥有相同的身份标识(identity),它们依然是同一个实体。 我们通过标识对对象进行区分,而不是属性,此时我们应该将标识作为主要的模型定义。同时我们需要保持简单的类定义,并且关注对象在其生命周期中的连续性和唯一标识性。 随着对象的改变,我们可能会跟踪这样的改变,比如什么时候发生了改变,发生了什么改变,是谁做出的改变等。我们应该慎重对待在对象整个生命周期中所发生的合法改变。 唯一的身份标识和可变性(mutability)特征将实体对象和值对象(Value Objects)区分开来。 n 很 多 时 候 , 一 个 领 域 概 念 应 该 建 模 成 值 对 象 , 而 不 是 实 体 对 象 。n 实 体 和 值 对 象 是 领 域 模 型 概 念 , 而 不 是 数 据 存 储 模 型 概 念 。 Value Objects(值对象) 值对象的特征n 它 度 量 或 者 描 述 了 领 域 中 的 一 件 东 西 。n 它 可 以 作 为 不 变 量 。n 它 将 不 同 的 相 关 的 属 性 组 合 成 一 个 概 念 整 体 n 当 度 量 和 描 述 改 变 时 , 可 以 用 另 一 个 值 对 象 予 以 替 换n 它 可 以 和 其 他 值 对 象 进 行 相 等 性 比 较n 它 不 会 对 协 作 对 象 造 成 副 作 用 。 当我们只关心一个模型元素的属性时,应把它归类为值对象。我们应该使这个模型元素能够表示出其属性的意义,并为它提供相关功能。值对象应该是不可变的。不要为它分配任何标识,而且不要把它设计成Entity那么复杂。 应该尽量使用值对象来建模而不是实体对象,即便一个领域概念必须建模成实体,在设计时也应该更偏向于将其作为值对象容器,而不是子实体容器。 实体对象与值对象是领域概念,而不是数据存储模型概念n 值 对 象 可 以 与 其 所 在 的 实 体 对 象 保 存 在 同 一 张 表 中 , 值 对 象 的 每 一 个 属 性 保 存 为 一 列 ; 值 对 象 也 可 以 独 立 于 其 所 在 的 实 体 对 象 保 存 在 另 一 张表 中 , 值 对 象 获 得 委 派 主 键 , 该 主 键 对 客 户 端 是 不 可 见 的 。 Entity和Value Object示例 Aggregates(聚合) 在具有复杂关联的模型中,要想保证对象更改的一致性是很困难的。不仅互不关联的对象需要遵守一些固定规则,而且紧密关联的各组对象也要遵守一些固定规则。然而,过于谨慎的锁定机制又会导致多个用户之间毫无意义地互相关绕,从而使系统不可用。在任何具有持久化数据存储的系统中,对数据进行修改的事务必须要有一个范围,而且要有一种保持数据一致性的方式。 聚合(Aggregate)是一组相关对象的集合,我们把它作为数据修改的单元。每个聚合都有一个根和一个边界,边界定义了聚合的内部都有什么,根则是聚合中所包含的一个特定实体。在聚合中,根是唯一允许外部对象保持对它的引用的元素,而边界内部的对象之间则可以互相引用。除根以外的其他Entity都有本地表示,但这些标识只有在聚合内部才需要加以区别,因为外部对象除了根Entity之外看不到其他对象。 聚合行为视为是一个整体,在每个事务完成时,必须要满足聚合内所应用的固定规则的要求,即保证数据变化的一致性。根实体最终检查固定规则;删除操作必须一次删除聚合边界之内的所有对象;当提交对聚合边界内部的任何对象的修改时,整个聚合中的所有固定规则都必须被满足。 原则:在一致性边界之内建模真正的不变条件;设计小聚合;通过唯一标识引用其他聚合;在边界之外使用最终一致性 尽量将根实体所包含的其他聚合建模成值对象,而不是实体。 Aggregates(聚合)示例 Domain Event(领域事件) Domain Event(领域事件)n 有 时 候 应 用 需 要 记 录 跟 踪 事 情 的 发 生n 领 域 事 件 经 常 被 建 模 为 Value Object, 但 这 些 Value Object并 不 能 被 共 享 , 因为 领 域 事 件 本 身 是 “ 唯 一 ” 的 。n 一 个 领 域 事 件 是 指 一 个 在 领 域 中 “ 有 意 义 ” 的 事 件 Hintsn UML四 色 原 型 中 有 一 个 相 近 概 念 , 称 为 时 刻 -时 段 原 型 (Moment-interval), 即表 示 事 物 在 某 个 时 刻 或 某 一 段 时 间 内 发 生 。 参考:四色原型 四色原型是诞生于90年代,现在被广泛使用的一种系统分析方法,如Borland的Together架构师版,准确地说,是由Peter Coad 和 Mark Mayfield首先提出,然后由David North拓展。 Repositories(资源库/仓储) 客户需要以一种符合实际的方式来获取对以存在的领域对象的引用。为每种需要全局访问的对象类型创建一个对象,这个对象就相当于该类型的所有对象在内存中的一个集合的“替身”。通过一个众所周知的接口来提供访问。提供添加和删除对象的方法,用这些方法来封装在数据存储中实际插入或删除数据的操作。提供根据具体标准来挑选对象的方法,并返回属性值满足查询标准的对象或对象集合(所返回的对象是完全实例化的),从而将实际的存储和查询技术封装起来。只为那些确实需要直接访问的聚合提供Repository。让客户始终聚焦于模型,而将所有对象的存储和访问操作交给Repository来完成。 Repository的接口应当采用领域通用语言。作为客户端,不应当知道数据库实现的细节。 Repository和DAO的作用类似,二者的主要区别:n DAO是 比 Repository更 低 的 一 层 , 包 含 了 如 何 从 数 据 库 中 提 取 数 据 的 代 码 。n Repository以 “ 领 域 ” 为 中 心 , 所 描 述 的 是 “ 领 域 语 言 ” 。 Repository把 ORM框 架 与 领 域 模型 隔 离 , 对 外 隐 藏 封 装 了 数 据 访 问 机 制 。 Repositories(资源库/仓储)示例public interface AccountRepository Account findAccount(String accountId); void addAccount(Account account);public class HibernateAccountRepository implements AccountRepository private HibernateTemplate hibernateTemplate; public HibernateAccountRepository(HibernateTemplate template) hibernateTemplate = template; public void addAccount(Account account) hibernateTemplate.save(account); public Account findAccount(final String accountId) return (Account) DataAccessUtils.uniqueResult(hibernateTemplate. findByNamedQueryAndNamedParam( “Account.findAccountByAccountId”, “accountId”, accountId); Services(领域服务) 当领域中的某个操作过程或转换过程不是实体或值对象的职责时,我们便应该将该操作放在一个单独的接口中,即领域服务。如果勉强地把这些重要的领域功能归为Entity或ValueObject的职责,那么不是歪曲了基于模型的对象的定义,就是人为地增加了一些无意义的对象。应确保领域服务和通用语言是一致的,并且保证它是无状态的。 正确区分领域服务(Domain Service)和应用服务(Application Service):n 我 们 不 应 把 业 务 逻 辑 置 于 应 用 服 务 , 但 我 们 会 把 业 务 逻 辑 置 于 领 域 服 务 中 。 (应 用 )服 务 要 做“ 薄 ” 。 n 领 域 服 务 职 责 : 跨 聚 合 实 例 业 务 逻 辑 ; 没 办 法 合 理 放 到 实 体 中 的 其 它 业 务 逻 辑 。n 应 用 服 务 职 责 : 跨 限 界 上 下 文 的 业 务 逻 辑 ; DTO转 换 ; 事 务 AOP、 权 限 AOP、 日 志 AOP、 异常 AOP; 外 部 系 统 访 问 ( 邮 件 、 消 息 队 列 ) 。n 领 域 服 务 设 计 原 则 : 用 来 组 织 业 务 逻 辑 , 面 向 业 务 逻 辑 ; 细 粒 度 ; 内 部 视 图 看 系 统 ; 一 个 请求 对 应 多 个 服 务 的 多 个 方 法 ; 服 务 之 间 会 存 在 依 赖 ;n 应 用 服 务 设 计 原 则 : 用 来 封 装 业 务 逻 辑 ; 面 向 用 例 ; 粗 粒 度 ; 外 部 视 图 看 系 统 ; 一 个 请 求 对应 一 个 方 法 ; 服 务 之 间 互 不 依 赖 。n 应 用 服 务 和 领 域 服 务 区 分 非 常 敏 感 , 有 时 候 需 要 在 快 速 性 /方 便 性 上 做 折 衷 。 Services(领域服务)示例public interface MoneyTransferService BankingTransaction transfer(String fromAccountId, String toAccountId, double amount);public class MoneyTransferServiceImpl implements MoneyTransferService private final AccountRepository accountRepository; private final BankingTransactionRepository bankingTransactionRepository; public MoneyTransferServiceImpl(AccountRepository accountRepository, BankingTransactionRepository bankingTransactionRepository) BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) 应用服务、领域服务和基础设施服务 Factories(工厂) 当创建一个对象或创建整个聚合时,如果创建工作很复杂,或者暴露了过多的内部结构,则可以使用Factory进行封装。应该将创建复杂对象的实例和聚合的职责转移到一个单独的对象,这个对象本身在领域模型中可能没有职责,但它仍是领域设计的一部分。 不同类型的工厂模式:n 工 厂 类n 工 厂 方 法 Modules(模块) Module为人们提供了两种观察模型的方式,一是可以在Module中查看细节,而不会被整个模型淹没,二是观察Module之间的关系,而不考虑其内部细节。 模块之间应该是低耦合的,而在模块内部则是高内聚的。模块并不仅仅是代码的划分,而且也是概念的划分。一个人一次考虑的事情是有限的(因此才有低耦合);不连贯的思想和“一锅粥”似的思想同样难于理解(因此才有高内聚)。 选择能够描述系统的Module,并使之包含一个内聚的概念集合。这通常会实现Module之间的低耦合,但如果效果不理想,则应寻找一种更改模型的方式来消除概念之间的耦合,或者找到一个可作为Module基础的概念,基于这个概念组织的模型可以以一种有意义的方式将元素集中到一起。找到一种低耦合的概念组织方式,从而可以相互独立地理解和分析这些概 念。对模型进行精化,直到可以根据高层领域概念对模型进行划分,同时相应的代码也不会产生耦合。 Module的名称应该是领域通用语言中的术语。模块及其名称应反映出领域的深层知识。 培训内容领 域 驱 动 设 计 简 介领 域 通 用 语 言领 域 驱 动 设 计 的 构 造 块领 域 驱 动 设 计 编 程 实 践CQRS架 构模 型 驱 动 开 发 概念辨析-VO/DTO/DO/PO(一) View Object(视图对象):视图对象,用于展示层,其作用是把某个指定页面(或组件)的所有数据封装起来。 Data Transfer Object(数据传输对象):这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。 Domain Object(领域对象):从现实世界中抽象出来的有形或无形的业务实体、值对象或领域服务。 Persistent Object(持久化对象):跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。 Ref: http:/ 概念辨析-VO/DTO/DO/PO(二) VO与DTO:绝大多数应用场景下,VO与DTO的属性值基本一致,但对于设计层面来说,概念上还是存在VO和DTO的区别,DTO代表服务层需要接收的数据和返回的数据,而VO代表展示层需要显示的数据。n 示 例 : 服 务 层 有 一 个 getUser的 方 法 返 回 一 个 系 统 用 户 , 其 中 有 一 个 属 性 是gender(性 别 ), 对 于 服 务 层 来 说 , 它 只 从 语 义 上 定 义 : 1-男 性 , 2-女 性 , 0-未指 定 , 而 对 于 展 示 层 来 说 , 它 可 能 需 要 用 “ 帅 哥 ” 代 表 男 性 , 用 “ 美 女 ” 代 表女 性 , 用 “ 秘 密 ” 代 表 未 指 定 。 说 到 这 里 , 可 能 你 还 会 反 驳 , 在 服 务 层 直 接 就返 回 “ 帅 哥 美 女 ” 不 就 行 了 吗 ? 对 于 大 部 分 应 用 来 说 , 这 不 是 问 题 , 但 设 想 一下 , 如 果 需 求 允 许 客 户 可 以 定 制 风 格 , 而 不 同 风 格 对 于 “ 性 别 ” 的 表 现 方 式 不一 样 , 又 或 者 这 个 服 务 同 时 供 多 个 客 户 端 使 用 ( 不 同 门 户 ) , 而 不 同 的 客 户 端对 于 表 现 层 的 要 求 有 所 不 同 , 那 么 , 问 题 就 来 了 。 再 者 , 回 到 设 计 层 面 上 分 析 , 从 职 责 单 一 原 则 来 看 , 服 务 层 只 负 责 业 务 , 与 具 体 的 表 现 形 式 无 关 , 因 此 , 它返 回 的 DTO, 不 应 该 出 现 与 表 现 形 式 的 耦 合 。n 实 现 层 面 是 否 需 要 区 分 二 者 概 念 ? 具 体 问 题 具 体 分 析 概念辨析-VO/DTO/DO/PO(三) DTO与DO:DTO是展示层和服务层之间的数据传输对象(可以认为是两者之间的协议),而DO是对现实世界各种业务角色的抽象,这就引出了两者在数据上的区别,例如UserInfo和User,对于一个getUser方法来说,本质上它永远不应该返回用户的密码,因此UserInfo至少比User少一个password的数据。而在领域驱动设计中,DO不是简单的POJO,它具有领域业务逻辑。 n 在 设 计 层 面 , 展 示 层 向 服 务 层 传 递 的 DTO与 服 务 层 返 回 给 展 示 层 的 DTO在 概 念 上 是 不 同 的 (如 返 回 UserInfo应 该 不 包 含 password, 但 创 建User传 入 的 参 数 需 要 包 含 password), 但 在 实 现 层 面 , 我 们 通 常 很 少 会 这 样 做 ( 定 义 两 个 UserInfo, 甚 至 更 多 ) , 因 为 这 样 做 并 不 见 得 很 明智 , 我 们 完 全 可 以 设 计 一 个 完 全 兼 容 的 DTO, 在 服 务 层 接 收 数 据 的 时 候 , 不 该 由 展 示 层 设 置 的 属 性 ( 如 订 单 的 总 价 应 该 由 其 单 价 、 数 量 、 折扣 等 决 定 ) , 无 论 展 示 层 是 否 设 置 , 服 务 层 都 一 概 忽 略 , 而 在 服 务 层 返 回 数 据 时 , 不 该 返 回 的 数 据 ( 如 用 户 密 码 ) , 就 不 设 置 对 应 的 属 性 。n 为 什 么 不 在 服 务 层 中 直 接 返 回 DO: DO具 有 一 些 不 应 该 让 展 示 层 知 道 的 数 据 ; DO具 有 业 务 方 法 , 如 果 直 接 把 DO传 递 给 展 示 层 , 展 示 层 的 代码 就 可 以 绕 过 服 务 层 直 接 调 用 它 不 应 该 访 问 的 操 作 , 对 于 基 于 AOP拦 截 服 务 层 来 进 行 访 问 控 制 的 机 制 来 说 , 这 问 题 尤 为 突 出 , 而 在 展 示 层 调用 DO的 业 务 方 法 也 会 因 为 事 务 的 问 题 , 让 事 务 难 以 控 制 ; ORM框 架 ( 如 Hibernate) “ 延 迟 加 载 ” 技 术 , 如 果 直 接 把 DO暴 露 给 展 示 层 , 对 于 大 部 分 情 况 , 展 示 层 不 在 事 务 范 围 之 内 , 如 果 其 尝 试 在 Session关 闭 的 情 况 下 获 取 一 个 未 加 载 的 关 联 对 象 , 会 出 现 运 行 时 异 常 ( 对 于Hibernate来 说 , 就 是 LazyInitiliaztionException) ; 从 设 计 层 面 来 说 , 展 示 层 依 赖 于 服 务 层 , 服 务 层 依 赖 于 领 域 层 , 如 果 把 DO暴 露 出 去 , 就会 导 致 展 示 层 直 接 依 赖 于 领 域 层 , 这 虽 然 依 然 是 单 向 依 赖 , 但 这 种 跨 层 依 赖 会 导 致 不 必 要 的 耦 合 。n DTO应 该 是 一 个 “ 扁 平 的 二 维 对 象 ” 概念辨析-VO/DTO/DO/PO(四) DO与PO:DO和PO在绝大部分情况下是一一对应的,PO是只含有get/set方法的POJO,但某些场景还是能反映出两者在概念上存在本质的区别。n DO在 某 些 场 景 下 不 需 要 进 行 显 式 的 持 久 化 , 例 如 利 用 策 略 模 式 设 计 的 商 品 折 扣 策 略 , 会 衍 生 出 折 扣 策 略 的 接 口 和 不 同 折 扣 策 略 实 现 类 , 这些 折 扣 策 略 实 现 类 可 以 算 是 DO, 但 它 们 只 驻 留 在 静 态 内 存 , 不 需 要 持 久 化 到 持 久 层 , 因 此 , 这 类 DO是 不 存 在 对 应 的 PO的 。 同 样 的 道 理 ,某 些 场 景 下 , PO也 没 有 对 应 的 DO, 例 如 老 师 Teacher和 学 生 Student存 在 多 对 多 的 关 系 , 在 关 系 数 据 库 中 , 这 种 关 系 需 要 表 现 为 一 个 中 间 表 , 也 就 对 应 有 一 个 TeacherAndStudentPO的 PO, 但 这 个 PO在 业 务 领 域 没 有 任 何 现 实 的 意 义 , 它 完 全 不 能 与 任 何 DO对 应 上 。 这 里 要 特 别 声 明 ,并 不 是 所 有 多 对 多 关 系 都 没 有 业 务 含 义 , 这 跟 具 体 业 务 场 景 有 关 , 例 如 : 两 个 PO之 间 的 关 系 会 影 响 具 体 业 务 , 并 且 这 种 关 系 存 在 多 种 类 型 ,那 么 这 种 多 对 多 关 系 也 应 该 表 现 为 一 个 DO, 又 如 : “ 角 色 ” 与 “ 资 源 ” 之 间 存 在 多 对 多 关 系 , 而 这 种 关 系 很 明 显 会 表 现 为 一 个 DO“权限 ” 。n 某 些 情 况 下 , 为 了 某 种 持 久 化 策 略 或 者 性 能 的 考 虑 , 一 个 PO可 能 对 应 多 个 DO, 反 之 亦 然 。 例 如 客 户 Customer有 其 联
展开阅读全文