buy the book ribbon

序言

您可能想知道我们是谁,以及我们为什么要写这本书。

在 Harry 的上一本书《使用 Python 进行测试驱动开发》(O’Reilly 出版社)的结尾,他发现自己问了一堆关于架构的问题,例如,构建应用程序的最佳方式是什么,使其易于测试?更具体地说,如何确保您的核心业务逻辑被单元测试覆盖,并最大限度地减少您需要的集成测试和端到端测试的数量?他模糊地提到了“六边形架构”、“端口和适配器”和“函数式核心,命令式外壳”,但如果他诚实地说,他不得不承认这些都不是他真正理解或在实践中做过的事情。

然后他很幸运地遇到了 Bob,他拥有所有这些问题的答案。

Bob 最终成为一名软件架构师,因为他的团队中没有人做这件事。事实证明他在这方面很糟糕,但他幸运地遇到了 Ian Cooper,后者教会了他编写代码和思考代码的新方法。

管理复杂性,解决业务问题

我们都在 MADE.com 工作,这是一家欧洲电子商务公司,在线销售家具;在那里,我们应用本书中的技术来构建分布式系统,以模拟现实世界的业务问题。我们的示例领域是 Bob 为 MADE 构建的第一个系统,本书旨在记录我们必须教给新程序员的所有东西,当他们加入我们的团队时。

MADE.com 运营着全球货运合作伙伴和制造商的供应链。为了保持低成本,我们努力优化库存到我们仓库的交付,以便我们不会有未售出的商品堆放在那里。

理想情况下,您想购买的沙发将在您决定购买的当天到达港口,我们将直接运送到您家,而无需存放。正确把握时机是一个棘手的平衡行为,因为货物需要三个月才能通过集装箱船运到。在此过程中,物品会被损坏或被水损坏,风暴会导致意外延误,物流合作伙伴会 mishandle 货物,文书工作会丢失,客户会改变主意并修改订单等等。

我们通过构建智能软件来解决这些问题,这些软件代表了现实世界中发生的各种操作,以便我们可以尽可能多地自动化业务。

为什么选择 Python?

如果您正在阅读本书,我们可能不需要说服您 Python 很棒,所以真正的问题是“Python 社区为什么需要这样一本书?” 答案是关于 Python 的普及性和成熟度:尽管 Python 可能是世界上增长最快的编程语言,并且正在接近绝对受欢迎程度排行榜的榜首,但它才刚刚开始着手解决 C# 和 Java 世界多年来一直在研究的各种问题。初创公司成为真正的企业;Web 应用程序和脚本自动化正在成为(小声说)企业 软件

在 Python 世界中,我们经常引用 Python 之禅:“应该有一种——而且最好只有一种——显而易见的方法来做到这一点。”[1] 不幸的是,随着项目规模的增长,最显而易见的做事方式并不总是帮助您管理复杂性和不断变化的需求的方式。

我们在本书中讨论的所有技术和模式都不是新的,但它们对 Python 世界来说大多是新的。本书并不能取代该领域的经典著作,例如 Eric Evans 的《领域驱动设计》或 Martin Fowler 的《企业应用架构模式》(均由 Addison-Wesley Professional 出版)——我们经常参考这些著作,并鼓励您去阅读。

但文献中所有经典的示例代码往往都是用 Java 或 C++/# 编写的,如果您是 Python 人,并且很长时间没有使用过这些语言(或者实际上从未用过),那么这些代码清单可能会非常……令人费解。另一个经典文本 Fowler 的《重构》(Addison-Wesley Professional 出版社)最新版使用 JavaScript 是有原因的。

TDD、DDD 和事件驱动架构

为了管理复杂性,我们知道有三种工具,按知名度排序:

  1. 测试驱动开发 (TDD) 帮助我们构建正确的代码,并使我们能够重构或添加新功能,而无需担心回归。但很难充分利用我们的测试:我们如何确保它们尽可能快地运行?我们如何从快速、无依赖的单元测试中获得尽可能多的覆盖率和反馈,并最大限度地减少速度较慢、不稳定的端到端测试的数量?

  2. 领域驱动设计 (DDD) 要求我们将精力集中在构建业务领域的良好模型上,但我们如何确保我们的模型不会被基础设施问题所累,并且不会变得难以更改?

  3. 通过消息集成的松耦合(微)服务(有时称为响应式微服务)是管理跨多个应用程序或业务领域的复杂性的成熟答案。但如何使它们与 Python 世界的既定工具(Flask、Django、Celery 等)相适应,并不总是显而易见的。

注意
如果您不使用(或不感兴趣)微服务,请不要灰心。我们讨论的绝大多数模式,包括大部分事件驱动架构材料,绝对适用于单体架构。

本书的目的是介绍几种经典的架构模式,并展示它们如何支持 TDD、DDD 和事件驱动服务。我们希望它能作为以 Pythonic 方式实现它们的参考,并且人们可以将其作为在该领域进一步研究的第一步。

本书的读者

以下是我们对您,亲爱的读者,的一些假设:

  • 您已经接触过一些相当复杂的 Python 应用程序。

  • 您已经看到了尝试管理这种复杂性带来的一些痛苦。

  • 您不一定了解 DDD 或任何经典的应用程序架构模式。

我们围绕一个示例应用程序构建章节,来探索架构模式。我们在工作中使用 TDD,因此我们倾向于先展示测试列表,然后是实现。如果您不习惯先进行测试,那么一开始可能会感觉有点奇怪,但我们希望您很快就会习惯在看到代码“如何构建”之前,先看到代码“如何使用”(即从外部)。

我们使用了一些特定的 Python 框架和技术,包括 Flask、SQLAlchemy 和 pytest,以及 Docker 和 Redis。如果您已经熟悉它们,那不会有什么坏处,但我们认为这不是必需的。本书的主要目标之一是构建一个架构,使特定的技术选择成为次要的实现细节。

您将学到的内容概述

本书分为两部分;以下是我们将涵盖的主题及其所在的章节。

#part1

领域建模和 DDD(第 1 章、2 章和 7 章)

在某种程度上,每个人都吸取了教训,即复杂的业务问题需要以代码的形式反映出来,即领域模型。但是,为什么总是很难做到这一点,而不会与基础设施问题、我们的 Web 框架或其他任何东西纠缠在一起?在第一章中,我们对领域建模和 DDD 进行了广泛概述,并展示了如何从一个没有外部依赖项的模型以及快速单元测试开始。稍后,我们将回到 DDD 模式,讨论如何选择正确的聚合,以及这种选择如何与数据完整性问题相关。

仓库、服务层和工作单元模式(第 2 章、4 章和 5 章)

在这三章中,我们介绍了三种密切相关且相互加强的模式,这些模式支持我们使模型摆脱无关依赖项的雄心。我们在持久存储周围构建了一个抽象层,并构建了一个服务层来定义我们系统的入口点并捕获主要的用例。我们展示了这一层如何轻松地为我们的系统构建精简的入口点,无论是 Flask API 还是 CLI。

关于测试和抽象的一些思考(第 3 章和 5 章)

在介绍第一个抽象(仓库模式)之后,我们借此机会对如何选择抽象以及它们在选择如何将我们的软件耦合在一起方面的作用进行一般性讨论。在介绍服务层模式之后,我们稍微谈谈如何实现测试金字塔,以及如何在尽可能高的抽象级别编写单元测试。

#part2

事件驱动架构(第 8-11 章)

我们介绍了另外三种相互加强的模式:领域事件、消息总线和处理器模式。领域事件是捕捉系统中的某些交互是其他交互的触发器的想法的载体。我们使用消息总线来允许操作触发事件并调用适当的处理器。我们继续讨论如何将事件用作微服务架构中服务之间集成的模式。最后,我们区分命令事件。我们的应用程序现在从根本上来说是一个消息处理系统。

命令查询职责分离 ([chapter_12_cqrs])

我们展示了命令查询职责分离的示例,无论是否使用事件。

依赖注入 ([chapter_13_dependency_injection])

我们整理了显式和隐式依赖项,并实现了一个简单的依赖注入框架。

附加内容

我如何从这里到达那里? ([epilogue_1_how_to_get_there_from_here])

当您从头开始展示一个简单的示例时,实现架构模式总是看起来很容易,但是你们中的许多人可能想知道如何将这些原则应用于现有软件。我们将在尾声中提供一些指导,以及一些进一步阅读的链接。

示例代码和一起编写代码

您正在阅读一本书,但您可能会同意我们的观点,即学习代码的最佳方式是编写代码。我们从与人结对编程、与他们一起编写代码以及边做边学中学到了我们所知道的大部分知识,我们希望在这本书中尽可能地为您重现这种体验。

因此,我们围绕一个示例项目构建了本书(尽管我们有时会加入其他示例)。我们将随着章节的进展逐步构建这个项目,就好像您与我们结对编程,并且我们在每个步骤都解释我们正在做什么以及原因。

但是,要真正掌握这些模式,您需要摆弄代码并感受它是如何工作的。您将在 GitHub 上找到所有代码;每一章都有自己的分支。您还可以在 GitHub 上找到分支列表

以下是您可以与本书一起编写代码的三种方式:

  • 启动您自己的仓库,并尝试按照我们的方式构建应用程序,遵循本书列表中的示例,并偶尔查看我们的仓库以获取提示。但是,请注意:如果您读过 Harry 的上一本书并一起编写了代码,您会发现本书要求您自己弄清楚更多内容;您可能需要非常依赖 GitHub 上的工作版本。

  • 尝试将每个模式逐章应用于您自己的(最好是小型/玩具)项目,看看是否可以使其适用于您的用例。这是高风险/高回报(而且非常费力!)。为您的项目的具体情况使事情正常运行可能需要相当多的工作,但另一方面,您可能会学到最多的东西。

  • 为了减少工作量,在每一章中,我们都会概述一个“读者练习”,并指向一个 GitHub 位置,您可以在其中下载该章的一些部分完成的代码,其中包含一些缺失的部分供您自己编写。

特别是如果您打算在自己的项目中应用其中一些模式,那么完成一个简单的示例是安全练习的好方法。

提示
至少,在您阅读每一章时,请对我们仓库中的代码进行 git checkout。能够跳转进去并查看实际工作应用程序中的代码将有助于回答您在阅读过程中遇到的许多问题,并使一切更加真实。您将在每一章的开头找到有关如何执行此操作的说明。

许可证

代码(和本书的在线版本)根据 Creative Commons CC BY-NC-ND 许可证获得许可,这意味着您可以自由复制和与您喜欢的任何人共享,用于非商业目的,只要您注明出处即可。如果您想重复使用本书中的任何内容,并且对许可证有任何疑虑,请通过 联系 O’Reilly。

印刷版许可证不同;请参阅版权页。

本书中使用的约定

本书中使用了以下排版约定:

斜体

表示新术语、URL、电子邮件地址、文件名和文件扩展名。

等宽字体

用于程序列表,以及段落中引用程序元素,例如变量名或函数名、数据库、数据类型、环境变量、语句和关键字。

等宽粗体

显示用户应按字面键入的命令或其他文本。

等宽斜体

显示应替换为用户提供的值或由上下文确定的值的文本。

提示

此元素表示提示或建议。

注意

此元素表示一般注释。

警告

此元素表示警告或注意。

O’Reilly 在线学习

注意

40 多年来,O’Reilly Media 一直提供技术和商业培训、知识和见解,以帮助公司取得成功。

我们独特的专家和创新者网络通过书籍、文章、会议和我们的在线学习平台分享他们的知识和专业知识。O’Reilly 的在线学习平台使您可以按需访问实时培训课程、深入的学习路径、交互式编码环境以及来自 O’Reilly 和 200 多个其他出版商的大量文本和视频。有关更多信息,请访问 http://oreilly.com

如何联系 O’Reilly

请将有关本书的意见和问题发送给出版商:

  • O’Reilly Media, Inc.
  • 1005 Gravenstein Highway North
  • Sebastopol, CA 95472
  • 800-998-9938(美国或加拿大)
  • 707-829-0515(国际或本地)
  • 707-829-0104(传真)

我们为本书设立了一个网页,我们在其中列出了勘误表、示例和任何其他附加信息。您可以通过 https://oreil.ly/architecture-patterns-python 访问此页面。

发送电子邮件至 以评论或询问有关本书的技术问题。

有关我们的书籍、课程、会议和新闻的更多信息,请访问我们的网站 http://www.oreilly.com

在 Facebook 上找到我们:http://facebook.com/oreilly

在 Twitter 上关注我们:http://twitter.com/oreillymedia

在 YouTube 上观看我们:http://www.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 Zawaduk、Jostein Leira、Brandon Rhodes、Jazeps Basko、simkimsia、Adrien Brunet、Sergey Nosko、Dmitry Bychkov 以及更多人;如果我们在此列表中遗漏了您,我们深感抱歉。

超级-超级-感谢我们的编辑 Corbin Collins,感谢他温和的鼓励,以及作为读者不知疲倦的倡导者。同样-卓越地感谢制作人员 Katherine Tozer、Sharon Wilkey、Ellen Troutman-Zaig 和 Rebecca Demarest,感谢你们的奉献精神、专业精神和对细节的关注。本书因你们而得到极大的改进。

本书中遗留的任何错误自然都是我们自己的。


1. python -c "import this"