Python 的架构模式
Architecture Patterns with Python

原始链接: https://www.cosmicpython.com/book/preface.html

本书探讨了管理 Python 应用复杂性这一挑战,随着项目从初创公司发展到企业级软件,这一挑战日益突出。作者是 MADE.com 公司经验丰富的软件架构师,他们介绍了经典的架构模式,例如六边形架构、端口和适配器以及函数核心、命令式外壳,以支持测试驱动开发 (TDD)、领域驱动设计 (DDD) 和事件驱动架构。 本书提供了关于构建易于测试的应用程序的实用指导,重点是核心业务逻辑的单元测试,并尽量减少集成测试。它涵盖了关键模式,例如资源库、服务层、工作单元、领域事件、消息总线和处理程序模式,所有这些都通过 Python 示例进行了演示。 本书的目标读者是熟悉复杂应用程序的 Python 开发人员,无需事先了解 DDD,但需要具备一定的处理复杂性问题的经验。它指导读者构建一个示例应用程序,鼓励读者使用提供的 GitHub 代码库和练习进行编码。最终目标是使开发人员能够使用已建立的架构原则构建健壮且易于维护的 Python 系统。

一个 Hacker News 帖子讨论了《Python 架构模式》(cosmicpython.com) 这本书。一位 TypeScript 开发者评论说这本书的实用模式非常好,特别是用于测试的模拟工作单元/服务模式,以及关于领域特定事件命名的建议。他认为这是一份宝贵的、易于与团队分享的资源。另一位评论者表示同意,称这是一本很棒的 Python 编程书籍,尽管它故意没有使用静态类型。然而,一位用户报告了该书网址的 404 错误,并批评这是一个糟糕的架构选择。总的来说,评论表明这本书因其架构指导而受到好评,尤其是在测试和领域驱动设计方面。

原文

You may be wondering who we are and why we wrote this book.

At the end of Harry’s last book, Test-Driven Development with Python (O’Reilly), he found himself asking a bunch of questions about architecture, such as, What’s the best way of structuring your application so that it’s easy to test? More specifically, so that your core business logic is covered by unit tests, and so that you minimize the number of integration and end-to-end tests you need? He made vague references to "Hexagonal Architecture" and "Ports and Adapters" and "Functional Core, Imperative Shell," but if he was honest, he’d have to admit that these weren’t things he really understood or had done in practice.

And then he was lucky enough to run into Bob, who has the answers to all these questions.

Bob ended up as a software architect because nobody else on his team was doing it. He turned out to be pretty bad at it, but he was lucky enough to run into Ian Cooper, who taught him new ways of writing and thinking about code.

Why Python?

If you’re reading this book, we probably don’t need to convince you that Python is great, so the real question is "Why does the Python community need a book like this?" The answer is about Python’s popularity and maturity: although Python is probably the world’s fastest-growing programming language and is nearing the top of the absolute popularity tables, it’s only just starting to take on the kinds of problems that the C# and Java world has been working on for years. Startups become real businesses; web apps and scripted automations are becoming (whisper it) enterprise software.

In the Python world, we often quote the Zen of Python: "There should be one—​and preferably only one—​obvious way to do it." Unfortunately, as project size grows, the most obvious way of doing things isn’t always the way that helps you manage complexity and evolving requirements.

None of the techniques and patterns we discuss in this book are new, but they are mostly new to the Python world. And this book isn’t a replacement for the classics in the field such as Eric Evans’s Domain-Driven Design or Martin Fowler’s Patterns of Enterprise Application Architecture (both published by Addison-Wesley Professional)—which we often refer to and encourage you to go and read.

But all the classic code examples in the literature do tend to be written in Java or C++/#, and if you’re a Python person and haven’t used either of those languages in a long time (or indeed ever), those code listings can be quite…​trying. There’s a reason the latest edition of that other classic text, Fowler’s Refactoring (Addison-Wesley Professional), is in JavaScript.

TDD, DDD, and Event-Driven Architecture

In order of notoriety, we know of three tools for managing complexity:

  1. Test-driven development (TDD) helps us to build code that is correct and enables us to refactor or add new features, without fear of regression. But it can be hard to get the best out of our tests: How do we make sure that they run as fast as possible? That we get as much coverage and feedback from fast, dependency-free unit tests and have the minimum number of slower, flaky end-to-end tests?

  2. Domain-driven design (DDD) asks us to focus our efforts on building a good model of the business domain, but how do we make sure that our models aren’t encumbered with infrastructure concerns and don’t become hard to change?

  3. Loosely coupled (micro)services integrated via messages (sometimes called reactive microservices) are a well-established answer to managing complexity across multiple applications or business domains. But it’s not always obvious how to make them fit with the established tools of the Python world—​Flask, Django, Celery, and so on.

Note

Don’t be put off if you’re not working with (or interested in) microservices. The vast majority of the patterns we discuss, including much of the event-driven architecture material, is absolutely applicable in a monolithic architecture.

Our aim with this book is to introduce several classic architectural patterns and show how they support TDD, DDD, and event-driven services. We hope it will serve as a reference for implementing them in a Pythonic way, and that people can use it as a first step toward further research in this field.

A Brief Overview of What You’ll Learn

The book is divided into two parts; here’s a look at the topics we’ll cover and the chapters they live in.

#part1

Domain modeling and DDD (Chapters 1, 2 and 7)

At some level, everyone has learned the lesson that complex business problems need to be reflected in code, in the form of a model of the domain. But why does it always seem to be so hard to do without getting tangled up with infrastructure concerns, our web frameworks, or whatever else? In the first chapter we give a broad overview of domain modeling and DDD, and we show how to get started with a model that has no external dependencies, and fast unit tests. Later we return to DDD patterns to discuss how to choose the right aggregate, and how this choice relates to questions of data integrity.

Repository, Service Layer, and Unit of Work patterns (Chapters 2, 4, and 5)

In these three chapters we present three closely related and mutually reinforcing patterns that support our ambition to keep the model free of extraneous dependencies. We build a layer of abstraction around persistent storage, and we build a service layer to define the entrypoints to our system and capture the primary use cases. We show how this layer makes it easy to build thin entrypoints to our system, whether it’s a Flask API or a CLI.

Some thoughts on testing and abstractions (Chapter 3 and 5)

After presenting the first abstraction (the Repository pattern), we take the opportunity for a general discussion of how to choose abstractions, and what their role is in choosing how our software is coupled together. After we introduce the Service Layer pattern, we talk a bit about achieving a test pyramid and writing unit tests at the highest possible level of abstraction.

#part2

Event-driven architecture (Chapters 8-11)

We introduce three more mutually reinforcing patterns: the Domain Events, Message Bus, and Handler patterns. Domain events are a vehicle for capturing the idea that some interactions with a system are triggers for others. We use a message bus to allow actions to trigger events and call appropriate handlers. We move on to discuss how events can be used as a pattern for integration between services in a microservices architecture. Finally, we distinguish between commands and events. Our application is now fundamentally a message-processing system.

Command-query responsibility segregation ([chapter_12_cqrs])

We present an example of command-query responsibility segregation, with and without events.

Dependency injection ([chapter_13_dependency_injection])

We tidy up our explicit and implicit dependencies and implement a simple dependency injection framework.

Additional Content

How do I get there from here? ([epilogue_1_how_to_get_there_from_here])

Implementing architectural patterns always looks easy when you show a simple example, starting from scratch, but many of you will probably be wondering how to apply these principles to existing software. We’ll provide a few pointers in the epilogue and some links to further reading.

Example Code and Coding Along

You’re reading a book, but you’ll probably agree with us when we say that the best way to learn about code is to code. We learned most of what we know from pairing with people, writing code with them, and learning by doing, and we’d like to re-create that experience as much as possible for you in this book.

As a result, we’ve structured the book around a single example project (although we do sometimes throw in other examples). We’ll build up this project as the chapters progress, as if you’ve paired with us and we’re explaining what we’re doing and why at each step.

Here are three ways you might code along with the book:

  • Start your own repo and try to build up the app as we do, following the examples from listings in the book, and occasionally looking to our repo for hints. A word of warning, however: if you’ve read Harry’s previous book and coded along with that, you’ll find that this book requires you to figure out more on your own; you may need to lean pretty heavily on the working versions on GitHub.

  • Try to apply each pattern, chapter by chapter, to your own (preferably small/toy) project, and see if you can make it work for your use case. This is high risk/high reward (and high effort besides!). It may take quite some work to get things working for the specifics of your project, but on the other hand, you’re likely to learn the most.

  • For less effort, in each chapter we outline an "Exercise for the Reader," and point you to a GitHub location where you can download some partially finished code for the chapter with a few missing parts to write yourself.

Particularly if you’re intending to apply some of these patterns in your own projects, working through a simple example is a great way to safely practice.

Tip

At the very least, do a git checkout of the code from our repo as you read each chapter. Being able to jump in and see the code in the context of an actual working app will help answer a lot of questions as you go, and makes everything more real. You’ll find instructions for how to do that at the beginning of each chapter.
联系我们 contact @ memedata.com