本书专注于介绍分解複杂问题空间的原则和实践,以及构成可维护解空间的实现模式和最佳实践。你将学习如何通过使用战术模式构建有效领域模型以及如何通过套用DDD的战略模式维持其完整性。
基本介绍
- 书名:领域驱动设计模式、原理与实践
- 作者:[美]Scott Millett
- 出版社:清华大学出版社
- 出版时间:2016
作者荐语
Scott Millett是Iglu.com的IT总监,从1.0版本开始就使用.NET工作了。他在2010年和2011年获得了ASP.NET MVP,并且还着有《ASP.NET设计模式》和《精通.NET企业项目开发:最新的模式、工具与方法》。
Nick Tune是用技术、协作和领域驱动设计为複杂业务问题提供解决方案的软体开发者。通过开发目标宏伟的产品以及与充满热情的人一起工作,他在寻求不断地自我提升。
目 录
第Ⅰ部分 领域驱动设计的原则与实践
第1章 什幺是领域驱动设计 3
1.1 为複杂问题域创建软体的挑战 4
1.1.1 未使用通用语言创建的代码 4
1.1.2 组织结构的缺乏 5
1.1.3 泥球模式将扼杀开发 5
1.1.4 缺乏对问题域的关注 5
1.2 领域驱动设计模式如何管理複杂性 6
1.2.1 DDD的战略模式 6
1.2.2 DDD的战术模式 8
1.2.3 问题空间与解空间 9
1.3 领域驱动设计的实践与原则 10
1.3.1 专注于核心领域 10
1.3.2 通过协作进行学习 10
1.3.3 通过探索和实验来创建模型 10
1.3.4 通信 11
1.3.5 理解模型的适用性 11
1.3.6 让模型持续发展 11
1.4 领域驱动设计的常见误区 12
1.4.1 战术模式是DDD的关键 12
1.4.2 DDD是一套框架 12
1.4.3 DDD是一颗灵丹妙药 12
1.5 要点 13
第2章 提炼问题域 15
2.1 知识提炼与协作 15
2.1.1 通过通用语言达成共识 16
2.1.2 领域知识的重要性 17
2.1.3 业务分析员的角色 17
2.1.4 一个持续过程 17
2.2 与领域专家一起获得领域见解 18
2.2.1 领域专家与业务相关人员的对比 18
2.2.2 对于业务的更深刻理解 18
2.2.3 与你的领域专家互动 19
2.3 有效提炼知识的模式 19
- 2.3.1专注在最有意思的对话上 19
- 2.3.2从用例开始 19
- 2.3.3提出有力的问题 20
- 2.3.4草图 20
- 2.3.5CRC卡 21
- 2.3.6延迟对模型中概念的命名 21
- 2.3.7行为驱动开发 21
- 2.3.8快速成型 23
- 2.3.9查看基于纸面的系统 23
- 2.4查看现有模型 23
- 2.4.1理解意图 24
- 2.4.2事件风暴 24
- 2.4.3影响地图 25
- 2.4.4理解业务模型 26
- 2.4.5刻意发现 27
- 2.4.6模型探讨漩涡 27
- 2.5要点 28
- 第3章专注于核心领域 31
- 3.1为何要分解一个问题域 31
- 3.2如何捕获问题的实质 32
- 3.2.1超越需求 32
- 3.2.2为达成什幺是核心内容的共识而捕获领域愿景 32
- 3.3如何专注于核心问题 33
- 3.3.1提炼问题域 34
- 3.3.2核心领域 35
- 3.3.3将你的核心领域当作一款产品而非一个项目 36
- 3.3.4通用域 36
- 3.3.5支撑域 37
- 3.4子域如何决定解决方案的形成 37
- 3.5并非一个系统的所有部分都会经过良好设计 38
- 3.5.1专注于清晰边界而非完美模型 38
- 3.5.2一开始核心领域不必总是需要是完美的 39
- 3.5.3构建用于替代而非重用的子域 39
- 3.6如果没有核心领域怎幺办 39
- 3.7要点 39
- 第4章模型驱动设计 41
- 4.1什幺是领域模型 41
- 4.1.1领域与领域模型的对比 42
- 4.1.2分析模型 43
- 4.1.3代码模型 43
- 4.1.4代码模型是领域模型的主要表现 44
- 4.2模型驱动设计 44
- 4.2.1预先设计的挑战 44
- 4.2.2团队建模 46
- 4.3使用通用语言将分析和代码模型绑定在一起 47
- 4.3.1语言的生存周期将大于软体 48
- 4.3.2业务语言 48
- 4.3.3开发人员和业务之间的转译 48
- 4.4基于通用语言进行协作 49
- 4.4.1通过使用具体示例来定製出语言 50
- 4.4.2教导你的领域专家专注在问题上而不要跳到解决方案 50
- 4.4.3塑造语言的最佳实践 51
- 4.5如何创建有效的领域模型 52
- 4.5.1不要让实情妨碍一个好模型 52
- 4.5.2仅对相关内容建模 53
- 4.5.3领域模型都是暂时有用的 53
- 4.5.4要十分清楚专业术语 54
- 4.5.5限制你的抽象 54
- 4.6何时套用模型驱动设计 55
- 4.6.1如果它不值得花费精力,则不要尝试对其建模 56
- 4.6.2专注于核心领域 56
- 4.7要点 56
- 第5章领域模型实现模式 59
- 5.1领域层 59
- 5.2领域模型实现模式 60
- 5.2.1领域模型 61
- 5.2.2事务脚本 64
- 5.2.3表模组 67
- 5.2.4活动记录 67
- 5.2.5贫血领域模型 67
- 5.2.6贫血领域模型和函式编程 68
- 5.3要点 71
- 第6章使用有界上下文维护领域模型的完整性 73
- 6.1单个模型的挑战 74
- 6.1.1模型的複杂性可能会增加 74
- 6.1.2多个团队处理单个模型 74
- 6.1.3模型语言中的歧义 75
- 6.1.4领域概念的适用範围 76
- 6.1.5集成遗留代码或第三方代码 78
- 6.1.6领域模型并非企业模型 78
- 6.2使用有界上下文划分和破除大模型 79
- 6.2.1定义模型的边界 81
- 6.2.2子域和有界上下文之间的差异 84
- 6.3实现有界上下文 85
- 6.4要点 88
- 第7章上下文映射 91
- 7.1一个现实情况的映射 92
- 7.1.1技术的现实 92
- 7.1.2组织的现实 93
- 7.1.3映射一个相关现实情况 94
- 7.1.4用X标记核心领域的位置 94
- 7.2认识有界上下文之间的关係 94
- 7.2.1防止损坏层 95
- 7.2.2共享核心 96
- 7.2.3开放宿主服务 96
- 7.2.4分道扬镳 97
- 7.2.5合作关係 98
- 7.2.6一种上游/下游关係 98
- 7.3传递上下文映射 99
- 7.4上下文映射的战略重要性 100
- 7.4.1保持完整性 100
- 7.4.2解决计画的基础 101
- 7.4.3理解所有权和职责 101
- 7.4.4揭示业务工作流中的混乱区域 101
- 7.4.5识别非技术障碍 101
- 7.4.6鼓励良好的沟通 101
- 7.4.7帮助加入的新员工 102
- 7.5要点 102
- 第8章应用程式架构 103
- 8.1应用程式架构 103
- 8.1.1分离应用程式的问题 103
- 8.1.2从领域的複杂性中进行抽象 104
- 8.1.3分层架构 104
- 8.1.4依赖倒置 105
- 8.1.5领域层 105
- 8.1.6应用程式服务层 105
- 8.1.7基础架构层 106
- 8.1.8跨层通信 106
- 8.1.9隔离测试 107
- 8.1.10不要在有界上下文之间共享数据结构 108
- 8.1.11应用程式架构与用于有界上下文的架构的对比 109
- 8.2应用程式服务 110
- 8.2.1应用程式逻辑与领域逻辑的对比 111
- 8.2.2定义和公开能力 112
- 8.2.3业务用例协作 112
- 8.2.4应用程式服务表示的是用例,而不是创建、读取、更新和删除 112
- 8.2.5作为实现详情的领域层 113
- 8.2.6领域报告 113
- 8.2.7读取模型与事务模型的对比 113
- 8.3应用程式客户端 115
- 8.4要点 117
- 第9章团队开始套用领域驱动设计通常会遇到的问题 119
- 9.1过分强调战术模式的重要性 120
- 9.1.1将相同架构用于所有的有界上下文 120
- 9.1.2力求战术模式尽善尽美 120
- 9.1.3错误估计构造块对于DDD的价值 120
- 9.1.4专注于代码而非DDD的原则 121
- 9.2缺失了DDD的真实价值:协作、通信和上下文 121
- 9.2.1由于低估上下文的重要性而产生大泥球 122
- 9.2.2未能成功创建UL将造成歧义和误解 122
- 9.2.3由于缺乏协作将只能设计专注于技术的解决方案 123
- 9.3在不重要的部分花费太多时间 123
- 9.4简单问题複杂化 123
- 9.4.1将DDD原则套用到具有少量业务预期的琐碎领域 124
- 9.4.2别将CRUD作为反模式 124
- 9.4.3将领域模型模式用于每一个有界上下文 124
- 9.4.4问一问自己:额外的複杂性是否值得 124
- 9.5低估套用DDD的成本 125
- 9.5.1尝试在没有积极专注的团队的情况下取得成功 125
- 9.5.2项目背后没有领域专家时的协作尝试 125
- 9.5.3在非叠代式开发方法中进行学习 125
- 9.5.4将DDD套用到每一个问题 126
- 9.5.5为不必要的纯粹性而牺牲实用主义 126
- 9.5.6寻求验证会浪费精力 126
- 9.5.7永远力求代码之美 127
- 9.5.8DDD关乎的是提供价值 127
- 9.6要点 127
- 第10章套用DDD的原则、实践与模式 129
- 10.1推广使用DDD 129
- 10.1.1培训团队 130
- 10.1.2与业务人员进行交流 130
- 10.2套用DDD的原则 131
- 10.2.1理解愿景 131
- 10.2.2捕获所需的行为 131
- 10.2.3理解环境的现实情况 132
- 10.2.4对解决方案建模 133
- 10.3探究和实验 139
- 10.3.1质疑假设 139
- 10.3.2建模是一项持续性活动 139
- 10.3.3不存在错误的模型 140
- 10.3.4灵活的代码有助于探索发现 140
- 10.4让隐式内容变得显式 140
- 10.4.1处理歧义 141
- 10.4.2为事物命名 143
- 10.5问题解决人先行,技术专家后行 143
- 10.6如何才能知道我在正确地工作 143
- 10.6.1好用就足够了 144
- 10.6.2实践、实践、实践 144
- 10.7要点 144
- 第Ⅱ部分战略模式:在有界上下文之间通信
- 第11章有界上下文集成介绍 149
- 11.1如何集成有界上下文 150
- 11.1.1有界上下文是独立自主的 150
- 11.1.2在代码层面集成有界上下文的挑战 151
- 11.1.3使用物理边界来强制实现整洁的模型 154
- 11.1.4集成遗留系统 155
- 11.2集成分散式有界上下文 158
- 11.2.1集成用于分散式有界上下文的策略 159
- 11.2.2资料库集成 159
- 11.2.3平面档案集成 160
- 11.2.4RPC 161
- 11.2.5讯息传递 162
- 11.2.6REST 162
- 11.3DDD使用分散式系统的挑战 162
- 11.4分散式事务将损害可扩展性和可靠性 165
- 11.4.1有界上下文不必彼此保持一致 166
- 11.4.2最终一致性 166
- 11.5事件驱动回响式DDD 167
- 11.5.1展示回响式解决方案的弹性和可扩展性 168
- 11.5.2异步讯息传递的挑战和取捨 169
- 11.5.3RPC还有价值吗 169
- 11.6SOA和回响式DDD 170
- 11.6.1将你的有界上下文视作SOA服务 171
- 11.6.2进一步处理微服务架构 174
- 11.7要点 175
- 第12章通过讯息传递集成 177
- 12.1讯息传递基础 178
- 12.1.1讯息汇流排 178
- 12.1.2可靠的讯息传递 180
- 12.1.3存储转发 180
- 12.1.4命令和事件 180
- 12.1.5最终一致性 181
- 12.2使用NServiceBus构建一个电子商务应用程式 182
- 12.2.1系统设计 183
- 12.2.2从Web应用程式传送命令 187
- 12.2.3处理命令和发布事件 196
- 12.2.4使用讯息传递网关让外部HTTP调用变得可靠 203
- 12.2.5实践中的最终一致性 211
- 12.2.6有界上下文会存储其本地所需的所有数据 212
- 12.2.7把所有内容都放在UI中 220
- 12.3维护讯息传递应用程式 223
- 12.3.1讯息版本管理 223
- 12.3.2监控和扩展 228
- 12.4将有界上下文与公共传输集成 231
- 12.4.1讯息传递桥 232
- 12.4.2公共传输 233
- 12.5要点 240
- 第13章通过使用RPC和REST的HTTP来集成 241
- 13.1为何选用HTTP 242
- 13.1.1没有平台耦合 243
- 13.1.2每个人都理解HTTP 243
- 13.1.3大量的成熟工具和库 243
- 13.1.4内部测试你的API 243
- 13.2RPC 244
- 13.2.1在HTTP上实现RPC 244
- 13.2.2选择一种RPC风格 259
- 13.3REST 260
- 13.3.1深入浅出地解释REST 260
- 13.3.2用于有界上下文集成的
- REST 263
- 13.3.3维护REST应用程式 297
- 13.3.4将REST用于有界上下文集成的缺点 298
- 13.4要点 299
- 第Ⅲ部分战术模式:创建有效的领域模型
- 第14章构造块领域建模介绍 303
- 14.1战术模式 304
- 14.2对领域建模的模式 305
- 14.2.1实体 305
- 14.2.2值对象 308
- 14.2.3领域服务 310
- 14.2.4模组 312
- 14.3生命周期模式 312
- 14.3.1聚合 312
- 14.3.2工厂 316
- 14.3.3存储库 316
- 14.4显露模式 317
- 14.4.1领域事件 317
- 14.4.2事件溯源 319
- 14.5要点 320
- 第15章值对象 323
- 15.1何时使用值对象 324
- 15.1.1表示描述性的、欠缺身份的概念 324
- 15.1.2增强明确性 325
- 15.2定义特徵 327
- 15.2.1欠缺身份 327
- 15.2.2基于特性的相等性 327
- 15.2.3富含行为 331
- 15.2.4内聚 331
- 15.2.5不可变 331
- 15.2.6可组合性 333
- 15.2.7自验证 335
- 15.2.8可测试 338
- 15.3常见的建模模式 339
- 15.3.1静态工厂方法 339
- 15.3.2微类型 341
- 15.3.3规避集合 343
- 15.4持久化 346
- 15.4.1NoSQL 346
- 15.4.2SQL 347
- 15.5要点 354
- 第16章实体 355
- 16.1理解实体 356
- 16.1.1具有身份和连贯性的领域概念 356
- 16.1.2上下文依赖 356
- 16.2实现实体 357
- 16.2.1分配标识符 357
- 16.2.2将行为推入到值对象和领域服务中 363
- 16.2.3验证并强制不变性 365
- 16.2.4专注于行为,而非数据 368
- 16.2.5避免“建模现实世界”的谬误 371
- 16.2.6分散式设计 371
- 16.3常见的实体建模原则和模式 373
- 16.3.1使用规範实现验证和不变条件 373
- 16.3.2避免状态模式;使用显式建模 376
- 16.3.3避免将接收器和设定器与备忘录模式结合使用 379
- 16.3.4选用无隐藏意外影响的功能 380
- 16.4要点 382
- 第17章领域服务 383
- 17.1理解领域服务 384
- 17.1.1何时使用领域服务 384
- 17.1.2领域服务解析 388
- 17.1.3避免使用贫血领域模型 389
- 17.1.4与应用程式服务对比 390
- 17.2利用领域服务 390
- 17.2.1服务层中 390
- 17.2.2领域中 391
- 17.3要点 397
- 第18章领域事件 399
- 18.1领域事件模式的实质 400
- 18.1.1已经发生了的重要领域事件 400
- 18.1.2回响事件 401
- 18.1.3可选的异步性 401
- 18.1.4内部事件与外部事件对比 402
- 18.2事件处理操作 403
- 18.2.1调用领域逻辑 403
- 18.2.2调用应用程式逻辑 404
- 18.3领域事件的实现模式 404
- 18.3.1使用.Net框架的事件模型 404
- 18.3.2使用记忆体中的汇流排 406
- 18.3.3UdiDahan的DomainEvents静态类 409
- 18.3.4返回领域事件 412
- 18.3.5使用IoC容器作为事件分发器 415
- 18.4测试领域事件 416
- 18.4.1单元测试 416
- 18.4.2套用服务层测试 417
- 18.5要点 419
- 第19章聚合 421
- 19.1管理複杂对象图形 422
- 19.1.1选用单一遍历方向 422
- 19.1.2合格的关联关係 424
- 19.1.3选用ID而不是对象引用 425
- 19.2聚合 428
- 19.2.1围绕领域不变条件进行设计 429
- 19.2.2高层次的领域抽象 429
- 19.2.3一致性边界 429
- 19.2.4选用较小的聚合 434
- 19.3定义聚合边界 435
- 19.3.1eBidder:线上拍卖案例研究 435
- 19.3.2与不变条件保持一致 437
- 19.3.3与事务和一致性保持一致 439
- 19.3.4忽略用户界面影响 440
- 19.3.5避免无用的集合与容器 441
- 19.3.6不要专注于HAS-A关係 441
- 19.3.7重构聚合 441
- 19.3.8满足业务用例——非现实环境 441
- 19.4实现聚合 442
- 19.4.1选择一个聚合根 442
- 19.4.2引用其他聚合 446
- 19.4.3实现持久化 450
- 19.4.4实现事务一致性 454
- 19.4.5实现最终一致性 455
- 19.4.6实现并发性 458
- 19.5要点 459
- 第20章工厂 461
- 20.1工厂的作用 461
- 20.1.1从构造中分离出套用 462
- 20.1.2封装内部事物 462
- 20.1.3隐藏创建类型的决策 464
- 20.1.4聚合上的工厂方法 466
- 20.1.5用于重构的工厂 467
- 20.1.6务实地使用工厂 469
- 20.2要点 469
- 第21章存储库 471
- 21.1存储库 471
- 21.2一种被误解的模式 473
- 21.2.1存储库是一种反模式吗 473
- 21.2.2领域模型和持久化模型之间的区别 474
- 21.2.3通用存储库 475
- 21.3聚合持久化策略 477
- 21.3.1使用能在不损坏领域模型的情况下将其映射到数据模型的持久化框架 478
- 21.3.2使用不能在不影响领域模型的情况下直接映射它的持久化框架 478
- 21.3.3公共接收器和设定器 479
- 21.3.4使用备忘录模式 480
- 21.3.5事件流 482
- 21.3.6求真务实 483
- 21.4存储库是一个明确的约定 483
- 21.5事务管理和工作单元 484
- 21.6保存或不保存 488
- 21.6.1持久化追蹤领域对象变更的框架 489
- 21.6.2必须将变更显式保存到聚合 490
- 21.7充当防止损坏层的存储库 491
- 21.8存储库的其他职责 491
- 21.8.1实体ID生成 492
- 21.8.2集合汇总 494
- 21.8.3并发性 494
- 21.8.4审计追蹤 498
- 21.9存储库反模式 498
- 21.9.1反模式:不要支持即席查询 498
- 21.9.2反模式:延迟载入是一种设计异味 499
- 21.9.3反模式:不要为了报告需要而使用存储库 499
- 21.10存储库实现 499
- 21.10.1持久化框架可以在不损坏领域模型的情况下将其映射到数据模型 500
- 21.10.2持久化框架不能在不损坏领域模型的情况下直接映射领域模型 550
- 21.11要点 586
- 第22章事件溯源 587
- 22.1将状态存储为快照的限制 588
- 22.2通过将状态存储为事件流来获得竞争优势 589
- 22.2.1时态查询 589
- 22.2.2投影 590
- 22.2.3快照 591
- 22.3源自事件的聚合 591
- 22.3.1构造 592
- 22.3.2持久化与再融合 596
- 22.4构建一个事件存储 603
- 22.4.1设计一种存储格式 604
- 22.4.2创建事件流 605
- 22.4.3附加到事件流 606
- 22.4.4查询事件流 606
- 22.4.5添加快照支持 607
- 22.4.6管理并发性 609
- 22.4.7一个基于SQLServer的事件存储 613
- 22.4.8构建你自己的事件存储是一个好主意吗 619
- 22.5使用专门构建的EventStore 619
- 22.5.1安装GregYoung的EventStore 619
- 22.5.2使用C#客户端库 620
- 22.5.3运行时态查询 624
- 22.5.4创建投影 627
- 22.6使用事件溯源的CQRS 629
- 22.6.1使用投影创建视图快取 630
- 22.6.2CQRS和事件溯源协作 630
- 22.7简要複述事件溯源的好处 631
- 22.7.1竞争性业务优势 631
- 22.7.2专注于表述性行为的聚合 631
- 22.7.3简化的持久化 632
- 22.7.4更好的调试 632
- 22.8衡量事件溯源的代价 632
- 22.8.1版本控制 632
- 22.8.2要学习的新概念和要磨练的技能 632
- 22.8.3需要学习和掌握的新技术 633
- 22.8.4大量的数据存储需求 633
- 22.9额外的学习资源 633
- 22.10要点 633
- 第Ⅳ部分有效应用程式的设计模式
- 第23章应用程式用户界面的架构设计 637
- 23.1设计考量 638
- 23.1.1占有式UI与构成式UI的对比 638
- 23.1.2HTMLAPI与数据API的对比 640
- 23.1.3客户端与伺服器端聚合/协作对比 641
- 23.2示例1:用于非分散式有界上下文的一个基于HTMLAPI的、伺服器端的UI 643
- 23.3示例2:用于分散式有界上下文的一个基于数据API的客户端UI 650
- 23.4要点 658
- 第24章CQRS:一种有界上下文的架构 659
- 24.1为两个上下文维护单个模型的挑战 660
- 24.2用于複杂有界上下文的一种更好的架构 661
- 24.3命令端:业务任务 662
- 24.3.1显式建模意图 662
- 24.3.2不受展现干扰所影响的模型 663
- 24.3.3处理业务请求 665
- 24.4查询端:领域报告 665
- 24.4.1直接映射到数据模型的报告 666
- 24.4.2从领域事件中构建的具体化视图 667
- 24.5对CQRS的误解 668
- 24.5.1CQRS很难 668
- 24.5.2CQRS是最终一致的 668
- 24.5.3模型需要源自事件 669
- 24.5.4命令应该是异步的 669
- 24.5.5CQRS仅适用于讯息传递系统 669
- 24.5.6需要将CQRS用于领域事件 669
- 24.6可以扩展应用程式的模式 669
- 24.6.1扩展读取端:一个最终一致的读取模型 670
- 24.6.2扩展写入端:使用异步命令 672
- 24.6.3对一切进行扩展 673
- 24.7要点 674
- 第25章命令:用于处理业务用例的应用程式服务模式 677
- 25.1区分应用程式逻辑和
- 领域逻辑 678
- 25.1.1应用程式逻辑 678
- 25.1.2来自应用程式服务角度的领域逻辑 690
- 25.2应用程式服务模式 690
- 25.2.1命令处理程式 690
- 25.2.2发布/订阅 694
- 25.2.3请求/回复模式 696
- 25.2.4async/await 698
- 25.3测试应用程式服务 699
- 25.3.1使用领域专业术语 699
- 25.3.2测试儘可能多的功能 700
- 25.4要点 702
- 第26章查询:领域报告 703
- 26.1有界上下文中的领域报告 704
- 26.1.1从领域对象中派生报告 704
- 26.1.2直接访问数据存储 710
- 26.1.3从事件流构建投影 716
- 26.2跨有界上下文的领域报告 723
- 26.2.1複合UI 723
- 26.2.2单独的报告上下文 724
- 26.3要点 726