新書推薦:
《
中国法律史学学说史(中国法学学术史丛书;国家出版基金项目)
》
售價:HK$
184.8
《
方尖碑(全2册)
》
售價:HK$
105.6
《
出土文献与汉唐法制史新论
》
售價:HK$
85.8
《
最美最美的博物书(全5册)
》
售價:HK$
160.6
《
戏剧影视表演专业原创小品合集
》
售價:HK$
96.8
《
在游戏中培养自立的孩子
》
售價:HK$
49.5
《
玄学与魏晋士人心态(精)--中华学术·有道
》
售價:HK$
85.8
《
硅、神经与智能体:人工智能的觉醒
》
售價:HK$
85.8
編輯推薦:
编辑推荐
随着Python的受欢迎程度不断增加,项目也变得越来越大且复杂。许多Python开发人员对高层次的软件设计模式感兴趣,例如六边形/整洁架构、事件驱动架构和领域驱动设计(DDD)推荐的战略模式。但是,将这些模式应用于Python项目中并不总是那么简单。
专家推荐
“这是社区一直在等待的书:Python中现代应用程序架构的转变是什么样子的!Harry和Bob展示了在一个优雅、动态的语言中依赖倒置原则的美感。”
——Brandon Rhodes
Python-patterns.guide作者
“到目前为止,几乎没有任何关于如何在Python中编写可维护的大规模系统的Python原生材料。这本书展示了Python完全胜任严肃的软件开发。”
——Hynek Schlawack
Pythonista、博客作者
和演讲者
內容簡介:
在这本实用指南中,本书介绍了经过验证的架构设计模式,帮助Python开发人员管理应用程序复杂性,并从测试套件中获得最Z大价值。每种模式都用优雅且惯用的Python实例进行了说明。模式包括: 依赖倒置及其与端口和适配器(六边形/整洁架构)的联系。领域驱动设计中的实体、值对象和聚合之间的区分。持久存储的仓储和工作单元模式。事件、命令和消息总线。命令查询责任分离(CQRS)。事件驱动架构和反应式微服务。
關於作者:
Harry Percival自2009年以来一直是一名Python程序员和TDD(测试驱动开发)以及XP(极限编程)的爱好者。他是OReilly出版的《Test-Driven Development with Python》的作者,该书的副标题《Obey the Testing Goat》更为人所知。Bob Gregory已经使用领域驱动设计开发事件驱动系统超过十年,所用语言包括C#、F#、Python和TypeScript。
目錄 :
目录
前言 1
第0 章 概述 11
第一部分 构建支持领域模型的架构
第1 章 领域建模 23
1.1 什么是领域模型 24
1.2 探索领域语言 .27
1.3 单元测试领域模型 28
1.3.1 数据类非常适合值对象 34
1.3.2 值对象和实体 36
1.4 并非一切都必须是对象:领域服务函数 38
1.4.1 基于Python 的魔法方法利用Python 使用我们的模型 39
1.4.2 异常也可以表达领域概念 40
第2 章 存储库模式 43
2.1 持久化我们的领域模型 .44
2.2 一些伪代码:我们需要什么 44
2.3 将DIP 应用于数据访问 .45
2.4 回顾:我们的模型 47
2.4.1 一般的ORM 方式:模型依赖于ORM 47
2.4.2 反转依赖关系:ORM 依赖于模型 .49
2.5 介绍存储库模式 52
2.5.1 抽象的存储库 53
2.5.2 什么是权衡 54
2.6 构建用于测试的假存储库现在变得微不足道 .58
2.7 在Python 中,什么是端口和适配器 59
2.8 总结 .60
第3 章 一个简要的插曲:关于耦合和抽象 .63
3.1 抽象状态有助于增加可测试性 64
3.2 选择正确的抽象 68
3.3 实现我们所选择的抽象 .69
3.3.1 使用伪造和依赖注入进行边缘到边缘测试 72
3.3.2 为什么不直接修补呢.74
3.4 总结 .77
第4 章 我们的第一个用例:Flask API 和服务层 79
4.1 将我们的应用程序连接到真实世界.81
4.2 第一个端到端测试 81
4.3 直接实现83
4.4 需要数据库检查的错误条件 84
4.5 引入服务层,并使用FakeRepository 对其进行单元测试 86
4.6 为什么一切都被称为服务 91
4.7 将事物放入文件夹以查看它们的归属 92
4.8 总结 .93
第5 章 高速挡和低速挡的TDD 97
5.1 我们的测试金字塔看起来怎么样 .98
5.2 应该将领域层测试转移到服务层吗.98
5.3 在决定编写哪种测试时 100
5.4 高速挡和低速挡 .101
5.5 将服务层测试与领域完全解耦 101
5.5.1 改进:在Fixture 函数中保留所有领域依存关系 .102
5.5.2 添加缺失的服务 103
5.6 将改进贯穿到E2E 测试中.104
5.7 总结 106
第6 章 工作单元模式 107
6.1 工作单元与存储库协作 109
6.2 通过集成测试驱动UoW . 110
6.3 工作单元及其上下文管理器 . 112
6.3.1 真正的工作单元使用SQLAlchemy 会话 . 113
6.3.2 用于测试的假工作单元 . 113
6.4 在服务层中使用工作单元(UoW) . 115
6.5 提交/ 回滚行为的显式测试 . 116
6.6 显式提交与隐式提交 117
6.7 示例:使用工作单元将多个操作分组到一个原子单元中 118
6.7.1 示例1:重新分配 118
6.7.2 示例2:更改批次数量 119
6.8 整理集成测试 119
6.9 总结 120
第7 章 聚合和一致性边界 123
7.1 为什么不直接使用电子表格运行所有内容 124
7.2 不变量、约束和一致性 124
7.3 什么是聚合 126
7.4 选择聚合.127
7.5 一个聚合= 一个存储库 130
7.6 关于性能.132
7.7 乐观并发控制与版本号 133
7.8 测试我们的数据完整性规则 .137
7.8.1 使用数据库事务隔离级别强制执行并发规则 138
7.8.2 悲观并发控制示例:SELECT FOR UPDATE 139
7.9 总结 140
7.10 第一部分概述 141
第二部分 事件驱动架构
第8 章 事件和消息总线 145
8.1 避免造成混乱 146
8.1.1 首先,让我们避免弄乱我们的Web 控制器 146
8.1.2 不要弄乱我们的模型147
8.1.3 或者尝试一下使用服务层 148
8.2 单一职责原则 149
8.3 全部都使用消息总线 149
8.3.1 模型记录事件 .149
8.3.2 事件是简单的数据类150
8.3.3 模型触发事件 .150
8.3.4 消息总线将事件映射到处理程序 .152
8.4 选项1:服务层从模型中获取事件并将其添加到消息总线 .153
8.5 选项2:服务层自己触发事件 .154
8.6 选项3:工作单元将事件发布到消息总线 155
8.7 总结 159
第9 章 深入探讨消息总线 163
9.1 新的需求引导我们走向新的架构 164
9.2 将服务层方法重构为消息处理程序167
9.2.1 消息总线现在从工作单元(UoW)收集事件 169
9.2.2 我们所有的测试也是根据事件编写的 171
9.2.3 丑陋的处理方法:消息总线必须返回结果 .172
9.2.4 修改API 以处理事件 .172
9.3 实现我们的新需求 .173
9.4 测试驱动新的处理程序 174
9.4.1 实现 .175
9.4.2 领域模型的新方法 177
9.5 选读:使用模拟消息总线对事件处理程序进行单元测试 .178
9.6 总结 181
9.6.1 我们实现了什么 181
9.6.2 我们为什么这么实现181
第10 章 命令与命令处理程序 183
10.1 命令与事件 .183
10.2 异常处理的差异 185
10.3 讨论:事件,命令,以及错误处理 187
10.4 同步错误恢复 190
10.5 总结 192
第11 章 事件驱动架构:使用事件集成微服务 195
11.1 分布式泥球和名词思维 196
11.2 分布式系统中的错误处理 199
11.3 替代方案:使用异步消息进行时间解耦 .200
11.4 使用Redis 发布/ 订阅通道进行集成 201
11.5 使用端到端测试进行全面测试驱动 202
11.5.1 Redis 是另一个围绕消息总线的轻量适配器 204
11.5.2 新发出的事件 205
11.6 内部事件与外部事件 .206
11.7 总结 206
第12 章 命令 C 查询责任分离 209
12.1 领域模型用于写 210
12.2 大部分用户不会买你的家具 211
12.3 Post/Redirect/Get 和CQS 213
12.4 抓紧你的午餐,朋友们 215
12.5 测试CQRS 视图 216
12.6 “显而易见”的替代方案1:使用现有存储库 217
12.7 领域模型未针对读取操作进行优化 218
12.8 “显而易见”的替代方案2:使用ORM 219
12.9 SELECT N 1 和其他性能考量 .219
12.10 是时候完全随波逐流了 .220
12.11 更改读取模型的实现很容易 224
12.12 总结 .225
第13 章 依赖注入和Bootstrapping 227
13.1 隐式依赖与显式依赖 .229
13.2 显式依赖不是很奇怪和Java 式吗 230
13.3 准备处理程序:带有闭包和部分函数的手动DI .233
13.4 使用类的备选方案 234
13.5 Bootstrap 脚本 235
13.6 消息总线在运行时提供处理程序 238
13.7 在程序入口使用Bootstrap 240
13.8 在测试中初始化DI 241
13.9 “正确”构建适配器:一个可用示例 243
13.9.1 定义抽象和具体实现 243
13.9.2 为你的测试完成一个假的版本 244
13.9.3 找出如何集成测试真实事物的方法 .245
13.10 总结 .247
后记 . 249
附录A 图表和表格汇总 267
附录B 项目结构模板 269
附录C 交换基础设施:全部使用CSV 279
附录D 存储库和工作单元Django 模式 . 285
附录E 验证 295
內容試閱 :
前言首先你可能想知道我们是谁以及为什么要写这本书。在Harry 的上一本书《Test-Driven Development with Python》(http://obeythe testinggoat.com)(O’Reilly)结尾时,他发现自己提出了一系列关于架构的问题,例如,什么是构建便于测试的应用程序的最佳方式?更具体地说,如何确保你的核心业务逻辑被单元测试覆盖,并尽量减少所需的集成和端到端测试的数量?在上本书里他模糊地提到了“六边形架构”“端口和适配器”“功能核心,命令外壳”,但如果他足够坦诚,他必须承认这些都不是他真正所理解或者是真正实践过的东西。然而他很幸运,他遇到了Bob,Bob 知道所有上述问题的答案。Bob 成为一名软件架构师的原因是,当时他们团队里没有任何一个人在做架构师的工作。事实上,一开始他干得并不好,但是他很幸运地遇到了Ian Cooper,Ian 教给他很多关于编写和思考代码的新方法。管理复杂性,解决业务问题我们都在为Made.com 工作,这是一家在线销售家具的欧洲电子商务公司。在那里,我们使用本书中的技术来构建分布式系统,以模拟现实世界的业务问题。我们的示例是Bob 为MADE 构建的第一个系统,这本书试图讲解当新程序员加入我们团队时,我们必须教给他们的所有东西。Made.com 运营着由货运合作伙伴和制造商组成的全球供应链。为了保持低成本,我们尝试优化仓库的库存交付模块,以免有未售出的商品堆积在仓库里。理想情况下,在你下单购买东西的当天,你买的沙发就会到达港口,我们会直接送到你家,而不需要入库储存。但是运送货物的集装箱船一般需要三个月时间才能到达,因此货物能否及时送到是一个棘手的问题。一路上可能出现很多意外,例如货物被破坏或者被水损坏、风暴导致意外延误、物流合作伙伴处理货物出错、文书丢失、客户改变主意并修改订单等。我们通过构建智能软件来解决这些问题,这些软件代表了现实世界中发生的各种操作,以便我们尽可能多地自动化业务。为什么选择Python?如果你正在阅读本书,那么可能不需要我们来告诉你Python 有多好,因此真正的问题是“为什么Python 社区需要这样的书?”答案在于Python 的受欢迎程度和成熟度:尽管Python 可能是世界上增长速度最快的编程语言,并且接近绝对受欢迎度排行榜的顶端,但它才刚刚开始解决C# 和Java 世界多年来一直在解决的各种问题。初创公司变成了真正的企业。使用Python 编写的和脚本化的自动化程序正在(悄悄地)成为企业化软件。在Python 世界中,我们经常引用Python 之禅:“任何问题应有一种且最好只有一种显而易见的解决方法。”注1 不幸的是,随着项目规模的增长,最显而易见的方法并不总是能帮助你管理好复杂性和不断变化的需求。我们在本书中讨论的所有技术和模式都不是一些新颖的概念,但对于Python 世界来说,它们大多是新颖的。而且,这本书不能替代这些相关领域的经典著作,比如Eric Evans 的《Domain-Driven Design》、Martin Fowler 的《Patterns of Enterprise Application Architecture》(这两本书均由Addison-Wesley Professional 出版),在本书中经常引用这两本书的内容,并且我们非常推荐你们去阅读这些书。但是所有经典文献中的示例代码都是使用Java、C 或者C# 进行编写的。如果你平时是使用Python 语言的,并且很长时间没有(或者是从来没有)使用过上述语言中的任何一种,那么这些代码可能会让你比较难以理解。因此,这就是为什么另一本经典书Fowler 的《Refactoring》(Addison-Wesley Professional 出版)里面的示例代码是使用JavaScript 语言编写的。测试驱动开发(TDD)、领域驱动设计(DDD)和事件驱动架构为了管理复杂性,我们知道有三种工具,按知名度排序如下:1. 测试驱动开发(TDD)帮助我们构建正确的代码,使我们能够重构或添加新性,而不必担心出现需要回滚的问题。但要充分利用我们的测试可能很困难:我们如何确保它们尽可能快地运行?我们如何从快速、无依赖的单元测试中获得尽可能多的覆盖率和反馈,并将较慢、不稳定的端到端测试数量降到最低?2. 领域驱动设计(DDD)要求我们集中精力构建一个良好的业务领域模型,但如何确保我们的模型不会受到基础设施问题的困扰,并且还不会变得难以更改?3. 通过消息集成的松耦合微服务(有时称为响应式微服务)是一种跨多个应用程序或业务领域管理复杂性的成熟解决方案。但是,如何使它们适用Python 世界中已建立的工具(如Flask、Django、Celery 等)则并不是那么容易。如果你不使用微服务(或对此不感兴趣),请暂时不要退出。我们在本书中讨论的大多数模式,包括许多事件驱动架构材料,绝对适用于单体架构。我们编写这本书的目的是介绍几种经典的架构模式,并展示它们如何支持TDD、DDD 和事件驱动服务。我们希望它能作为以Python 方式实现这些模式的参考,并且人们可以将其作为进一步研究该领域的第一步。谁应该读这本书亲爱的读者,我们假设你具备以下几点:? 你曾接触过一些相当复杂的Python 应用程序。? 你已经体验过管理这些复杂性所带来的痛苦。? 你不一定了解DDD 或任何经典的应用程序架构模式。我们围绕一个示例应用程序对设计模式进行探索,并逐章把整个架构模式构建起来。我们在工作中使用TDD,因此我们倾向于先列出所有的测试列表,随后进行内部实现。如果你不习惯于测试优先的方式,那么一开始你可能会感到有些奇怪,但我们希望你很快就会习惯在看到代码的内部构建之前,先看到代码“被使用”(即从外部调用)。我们使用一些定的Python 框架和技术,包括Flask、SQLAlchemy 和pytest,以及Docker 和Redis。如果你已经熟悉它们,这当然更好,但我们认为这不是必需的。本书的主要目标之一是构建一个体系结构,其他相关的定技术的实现细节则是次要的。概述本书的内容这本书分为两部分,下面来看看将要讨论的主题及其相关的章节。第一部分,构建支持领域模型的架构领域建模和DDD(第1 章和第7 章)复杂的业务问题需要以领域模型的形式反映在代码中,每个人都或多或少的经历过这样的教训。但领域模型为什么总是很难做到不与基础设施问题、Web 框架或任何其他问题纠缠在一起呢?在第1 章中,我们对领域建模和DDD 进行了宽泛的介绍,并展示了如何从一个没有外部依赖关系的模型和快速单元测试开始。然后,我们回到DDD 模式,讨论如何选择正确的聚合,以及这种选择与数据完整性问题的关系。存储库、服务层和工作单元模式(第2 章、第4 章和第5 章)在这三章中,我们介绍了三种密切相关且相互支持的模式,它们帮助我们维持这个目标:保持模型不受外部依赖性影响。我们围绕持久性存储构建了一个抽象层,并且构建了一个服务层来定义系统的入口点并捕获主要用例。我们展示了服务层如何轻松地构建系统的精简入口点,无论是使用Flask API 还是CLI。有关测试和抽象的一些思考(第3 章和第6 章)在介绍了第一个抽象(存储库模式)之后,我们借此机会对如何选择抽象以及它们在选择软件耦合方式中的作用进行了广泛讨论。在介绍服务层模式之后,我们将讨论如何实现测试金字塔,并在尽可能高的抽象层次上编写单元测试。第二部分,事件驱动架构事件驱动架构 (第8 ~ 11 章)我们介绍了三种相互支持的模式:领域事件、消息总线和处理程序模式。领域事件是捕捉系统中某些交互作为其他交互触发器的工具。我们使用消息总线允许操作触发事件,并调用适当的处理程序。接下来,我们讨论了如何将事件用作微服务架构中服务之间集成的模式。最后,我们对命令和事件进行了区分。我们的应用程序在这里基本上成为一个消息处理系统。命令查询职责分离 (第12 章)我们提供了一个命令查询责任分离的示例,包括有事件的和无事件的情况。依赖注入(第13 章)我们整理了显式和隐式依赖关系,并实现了一个简单的依赖注入框架。附录我如何从这里到达那里?(后记)当你从头开始展示一个简单的例子时,实现架构模式总是看起来很容易,但你们中的许多人可能会想知道如何将这些原则应用于现有软件。我们将在结语中提供一些提示,以及一些进一步阅读的链接。示例代码和编码尽管你正在通过读一本书来学习,但当我们说学习代码的最好方法是编码时,你可能会同意我们的观点。我们大部分的知识都是通过与他人结对、与他们一起编写代码以及通过实践学习的,我们希望在这本书中尽可能地重现这种体验。因此,我们围绕一个示例项目来介绍这本书(尽管有时会加入其他示例)。随着章节内容的深入,我们将逐步建立这个项目,就好像你已经在和我们一起结对编程,在创建项目过程中的每一步我们都会解释现在正在做什么,以及为什么这么做。但要真正掌握这些模式,你需要自己实现代码并了解其工作原理。你可以在GitHub上找到所有代码,每一章都有自己的分支。你也可以在GitHub 上的链接(https://github.com/cosmicpython/code/branches/all)上找到分支列表。以下是三种可以跟着本书一起学习编写代码的方式:? 启动你自己的代码库,并尝试按照我们在书中的示例构建应用程序,偶尔查看我们的代码库以获取提示。建议:如果你曾经读过Harry 的前一本书,并跟着一起编码,你会发现,这本书需要你自己更多的思考,你可能需要我们在GitHub 上已经实现的可以正常运行的代码。? 试着将每种模式逐章地应用到你自己的最好是小型的或者是业余项目中,看看你是否能使每一种模式适用你的用例。这是一种高风险/ 高回报的学习方式(除此之外,还要付出很大努力!)。你可能需要做相当多的工作才能使事情符合项目的具体情况,另外,你也可能会学到更多的知识。? 为了方便学习,我们在每一章中都概述了一个“读者练习”,并指向一个了GitHub 位置,在那里你可以下载本章的一些部分完成的代码,还有一些缺失的部分需要你自己编写。别是,如果你打算在自己的项目中应用其中一些模式,通过一个简单的示例进行操作是安全实践的好方法。至少,你可以在阅读每一章时,通过git checkout 命令将该章节的代码拉到本地。能够跳进去并在实际工作应用程序的上下文中查看代码,将有助于回答您在阅读过程中遇到的许多问题,并使一切更加真实。你可以在每一章的开头找到如何执行此操作的说明。许可证本书的代码(以及在线版本)是根据知识共享CC BY-NC-ND 许可证来授权的,这意味着你可以出于非商业目的自由复制并与任何人共享,只要你提供出处的引用。如果你想重用本书中的任何内容,并且对许可证有任何疑问,通过邮件permissions@oreilly.com 联系O’Reilly。发行版的许可证有所不同,请参阅英文原书的版权页。排版约定本书采用以下排版约定。斜体(Italic)表示新术语、URL、电子邮件地址、文件名和文件扩展名。等宽字体(Constant Width)表示程序清单,在段落内表示程序元素,例如变量、函数名称、数据库、数据类型、环境变量、语句和关键字。粗体等宽字体(Constant width bold)表示应由用户原封不动输入的命令或其他文本。斜体等宽字体(Constant width italic)表示应该替换成用户提供值的文本,或者由上下文决定的值。O’Reilly 在线学习平台(O’Reilly Online Learning)近40 年来,O’Reilly Media 致力于提供技术和商业培训、知识和卓越见解,来帮助众多公司取得成功。公司独有的专家和改革创新者网络通过O’Reilly 书籍、文章以及在线学习平台,分享他们的专业知识和实践经验。O’Reilly 在线学习平台按照您的需要提供实时培训课程、深入学习渠道、交互式编程环境以及来自O’Reilly 和其他200 多家出版商的大量书籍与视频资料。更多信息,请访问网站:https://www.oreilly.com/。联系我们任何有关本书的意见或疑问,请按照以下地址联系出版社。美国:O’Reilly Media, Inc.1005 Gravenstein Highway NorthSebastopol, CA 95472中国:北京市西城区西直门南大街2 号成铭大厦C 座807 室(100035)奥莱利技术咨询(北京)有限公司勘误、示例和其他信息可访问https://oreil.ly/architecture-patterns-python 获取。对本书的评论或技术疑问,可以发电子邮件到errata@oreilly.com.cn。欲了解本社图书和课程的新闻和信息,请访问我们的网站http://oreilly.com。我们的Facebook:http://facebook.com/oreilly。我们的Twitter:http://twitter.com/oreillymedia。我们的YouTube:http://youtube.com/oreillymedia。致谢首先要感谢我们的技术评审David Seddon、Ed Jung 和Hynek Schlawack。我们想说,能邀请到你们来做本书的技术评审是我们的荣幸。你们是如此的敬业、认真和严谨。你们每个人的观点都非常精妙,而且不同的观点之间都可以互相补充和完善。衷心感谢你们。非常感谢我们早期发行版的读者提供的意见和建议:Ian Cooper、Abdullah Ariff、Jonathan Meier、Gil Gon?alves、Matthieu Choplin、Ben Judson、James Gregory、?ukasz Lechowicz、Clinton Roy、Vitorino Araújo、Susan Goodbody、Josh Harwood、Daniel Butler、Liu Haibin、Jimmy Davies、Ignacio Vergara Kausel、Gaia Canestrani、Renne Rocha、pedroabi、Ashia ZawadukJostein Leira 和Brandon Rhodes等。如果该名单漏掉了某些人,我们对此深表歉意。再次感谢我们的编辑Corbin Collins 对细节的追求,以及感谢他乐此不疲地为我们传达读者们的心声。同样感谢出版团队的工作人员:Katherine Tozer、Sharon Wilkey、Ellen Troutman-Zaig 和Rebecca Demarest,感谢你们的奉献和专业精神,感谢你们对细节的关注。这本书因为有你们而有了不可估量的提升。另外,我们对本书可能还存在的任何错误感到抱歉。