|
1 | | -Proposed Book Title: |
2 | | -Proposed Book Subtitle: |
| 1 | +# Outline |
3 | 2 |
|
4 | | -* The Clean Architecture in Python |
5 | | -* Ports and Adapters with Python |
6 | | -* Enterprise Design Patterns in Python |
| 3 | +## Preface/Intro |
7 | 4 |
|
| 5 | +Who are we, why we are writing this book |
| 6 | +(all the args from the proposal, python's popularity, communicating well-understood patterns from the Java/C#/Enterprise world to a wider audience with nice, readable, pythonic code examples) |
8 | 7 |
|
| 8 | +> As Python grows in popularity as a language, typical projects are getting larger and more complex, and issues of software design and architecture become more salient. Patterns like "Ports and Adapters", as well Domain-Driven Design, Event-Driven programming, Command-Query Responsibility Segregation, which are well known in more "enterprisey" communities like Java and C#, are generating more interest in the Python community, but it's not always easy to see how these patterns translate into Python. (reviewing the classic "Gang of Four" design patterns book for example leads to the conclusion that half the patterns are artifacts of the Java/C++ syntax, and are simply not necessary in the more flexible and dynamic world of Python). |
9 | 9 |
|
10 | | -Author(s): |
11 | | -Author title(s) and affiliation(s): |
12 | | -Preferred mailing address(es): |
13 | | -Preferred phone number: |
14 | | -Preferred Email address(es): |
| 10 | +> In the Python world, we often quote the Zen of Python: "there should be one--preferably on only one--obvious way to do it". Unfortunately, as project complexity grows, the most obvious way of doing things isn't always the way that helps you manage complexity and evolving requirements. |
15 | 11 |
|
16 | | -Author Platform details: |
17 | | -Author biography and LinkedIn profile: |
| 12 | +> This book will provide an introduction to proven architectural design patterns that help you manage complexity, using concrete examples written in Python. It will explain how to avoid some of the unnecessary particularities of Java and C# syntax and implement these patterns in a "Pythonic" way. |
18 | 13 |
|
| 14 | +An overview of the central argument: |
19 | 15 |
|
20 | | -Author Web site/blog/Twitter: |
| 16 | +> Layer your system so that the low-level details depend on the high-level abstractions, not the other way around. |
21 | 17 |
|
22 | 18 |
|
23 | | -Why are you the best person to write this book? |
| 19 | +## Chapter 1: Domain modelling, and why do we always make it so hard for ourselves? |
24 | 20 |
|
| 21 | +Every so often someone says "where shall I put this new logic", and we all know the right answer: it should live in the domain model. So why does it always somehow end up in some gigantic controller function that's closely coupled with your web framework and database and third party apis and god knows what else? |
25 | 22 |
|
26 | | -Book Summary: |
27 | | -In one sentence, tell us why the audience will want to buy your book. |
| 23 | +Let's see what happens if we build everything around making sure our domain model is easy to work with and change. |
28 | 24 |
|
29 | | -Summarize what the book is about, like you would pitch it to a potential reader on the back cover. What makes your book unique in the marketplace? |
| 25 | +* Talk about domain modelling and DDD, work through an example (allocate-order) |
| 26 | +* Example code |
| 27 | +* crazy idea: Bob and I will record a video of (part of?) the allocate-order code writing as a TDD "kata", to illustrate how easy it is to do this stuff when you have no dependencies on databases, frameworks, etc (this will also save us from having to go slowly thru what tdd is in the book) |
30 | 28 |
|
31 | | -Briefly explain the technology and why it is important. |
| 29 | +code examples / patterns: some domain model objects (python3.7 dataclasses maybe?), a domain service / use case function, some "proper" unit tests. |
32 | 30 |
|
33 | | -Audience: |
34 | | -Explain who the primary audience is for your book. What skills can you assume they have mastered? |
| 31 | +related post from existing blog: https://io.made.com/introducing-command-handler/ |
35 | 32 |
|
36 | 33 |
|
37 | | -Please estimate as best you can how many people will use this technology? Please state any applicable statistics (e.g., web searches, web site traffic, blogs) indicating market use or market growth. |
38 | 34 |
|
| 35 | +## Chapter 2: persistence and the repository pattern |
39 | 36 |
|
40 | | -Please provide some scenarios that indicate how the audience will use your book. For example, will readers refer to it daily as a reference? Will they read it once to learn the concepts and then refer to it occasionally? |
| 37 | +The main ORM tool in the Python world is SQLAlchemy, and if you follow the default tutorial, you end up writing your model objects inheriting from `sqlalchemy.Table`, and soon your domain model is tightly coupled to your DB. |
41 | 38 |
|
42 | | -Key Topic Coverage: |
43 | | -What problems does this book solve for its users? |
| 39 | +But you don't have to! Demonstrate the alternative way to do metadata/mapping. |
44 | 40 |
|
45 | | -List the four or five topics covered or features included that will provide the greatest benefit to readers or will be the most likely to excite them? |
| 41 | +==> our ORM depends on the domain model, and not the other way around. an illustration of one of our key patterns/principles, the _Dependency Inversion Principle_ (the D in SOLID) |
46 | 42 |
|
47 | | -Other Book Features and Video Offerings: |
48 | | -Is there a companion web site? If so, what do you plan to include on the site? Would you be willing to participate in video offerings as well as workshops and training seminars? |
| 43 | +Also: repository pattern. choosing a simple abstraction (it's a dictionary) |
| 44 | +Also: first integration test |
49 | 45 |
|
50 | | -Competition: |
51 | | -What books or online resources compete with this book? Please list the title and author. In each case, how will your book be different or better in timing, content, coverage, approach, or tone? |
| 46 | +code examples / patterns: sqlalchemy metadata/mapping, repository pattern |
52 | 47 |
|
| 48 | +related post from existing blog: https://io.made.com/repository-and-unit-of-work-pattern-in-python/ |
53 | 49 |
|
54 | 50 |
|
55 | | -Book Outline (chapter level is fine): |
56 | 51 |
|
| 52 | +## Chapter 3: making ourselves available via a web API. Flask as a port (as in ports-and-adapters). Our first use case. Orchestration. Service layer |
57 | 53 |
|
58 | | -Specs and Schedule: |
59 | | -How many pages do you expect the book to be? |
| 54 | +We have a use case in mind, and a domain model that can do it, but how do we make it available to the outside world? |
60 | 55 |
|
61 | | -How long do you expect it to take you to write the book? |
| 56 | +start with a naive flask controller. evolve it to flask being an adapter to a use case function in our service/orchestration layer. |
| 57 | + |
| 58 | +* happy path, show the basic use case moving parts: create a database session, initialise our repository, load some objects, invoke our domain function, commit. |
| 59 | +* first acceptance test |
| 60 | +* handle error case, eg product does not exist. handle `KeyError` from repository, flask returns a 400 with nice erro json |
| 61 | +* but what if we have more error cases at this orchestration level? it'll be annoying to test everything with acceptance tests, and hard to unit test. |
| 62 | + |
| 63 | +==> introduce service layer. flask becomes an adapter. flask depends on the service layer, rather than the other way around (DIP once again) |
| 64 | + |
| 65 | +(later in the book we'll swap flask for asyncio, and show how easy it is) |
| 66 | + |
| 67 | +patterns: use case, service layer, port/adapter pattern for web, |
| 68 | + |
| 69 | +related post from existing blog: https://io.made.com/repository-and-unit-of-work-pattern-in-python/ |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +## Chapter 4: data integrity concerns 1: unit of work pattern |
| 74 | + |
| 75 | +What happens if we encounter an error during our allocation? eg out of stock, a domain error? We'd like to wrap our work up so that, either the entire order is allocated, or we abort and leave things in a "clean" state if anything goes wrong -- a classic case for a transaction/rollback. |
| 76 | + |
| 77 | +What's a Pythonic way of "doing transactions"? A context manager. demonstrate the _Unit of Work Pattern_ and show how it fits with _Repository_ |
| 78 | +But we also want a nice, Pythonic way of "doing transactions", of wrapping a block of code up into an atomic whole. |
| 79 | + |
| 80 | +discuss different options of unit of work, explicit/implicit rollbacks, dependency-inject uow. |
| 81 | + |
| 82 | +code examples / patterns: Unit Of Work (using a context manager) |
| 83 | + |
| 84 | +related post from existing blog: https://io.made.com/repository-and-unit-of-work-pattern-in-python/ |
| 85 | + |
| 86 | + |
| 87 | +## Chapter 5: data integrity concerns 2: choosing the right consistency boundary (Aggregate pattern) |
| 88 | + |
| 89 | +While we're thinking about data integrity, we now realise that our `allocate(order, shipments)` implementation which depends on all the shipments in the world won't scale if every order needs to lock the whole shipments table. We should only look at shipments that matter to that order. |
| 90 | + |
| 91 | +Moreover, we only need to allocate the order one line at a time (although maybe we want to roll back all the lines if we fail any one of them). |
| 92 | + |
| 93 | +This leads us on to discussing the _Aggregate_ pattern - by choosing `Product` as our aggregate, we choose a consistency boundary that allows us to be more clever about transactions. |
| 94 | + |
| 95 | +Also demonstrate how easy it is to refactor a domain model if it's not intermixed with infrastructure concerns. |
| 96 | + |
| 97 | +code examples / patterns: Aggregate |
| 98 | + |
| 99 | + |
| 100 | + |
| 101 | +## Chapter 6: CQRS |
| 102 | + |
| 103 | +The business comes along and supplies a new requirement: a dashboard showing the current allocation state of all shipments. Discuss how using the ORM naively leads to the _SELECT N+1_ antipattern, and use it as an opportunity to demonstrate _Command-Query-Responsiblity-Segregation (CQRS)_ -- read-only parts of our architecture can be implemented quite differently from the write side |
| 104 | + |
| 105 | +code examples / patterns: CQRS / raw sql queries |
| 106 | + |
| 107 | +related post from existing blog: https://io.made.com/commands-and-queries-handlers-and-views/ |
| 108 | + |
| 109 | + |
| 110 | + |
| 111 | +## Chapter 7: event-driven architecture part 1: events and the message bus |
| 112 | + |
| 113 | +Another new requirement: when allocation succeeds, someone should be emailed. But we don't want to have email-sending code be a potential cause of bugs/failures in our core model/state changes. introduce domain events and a message bus as a pattern for kicking off related work after a use case is complete. |
| 114 | + |
| 115 | +* discuss SRP, use case shouldn't have an _and_. leads naturally to events. |
| 116 | + |
| 117 | +code examples / patterns: events, handlers, message bus |
| 118 | + |
| 119 | +related post from existing blog: https://io.made.com/why-use-domain-events/ |
| 120 | + |
| 121 | + |
| 122 | + |
| 123 | +## Chapter 8: event-driven architecture part 2: domain events |
| 124 | + |
| 125 | +currently events are raised at the service layer. but what about something like "out of stock"? maybe that's an event that really belongs inside our domain, something that has business logic, not just orchestration. |
| 126 | + |
| 127 | +code examples / patterns: domain events raised by aggregate, unit of work with event tracking/ message bus integration |
| 128 | + |
| 129 | +related post from existing blog: https://io.made.com/why-use-domain-events/ |
| 130 | + |
| 131 | + |
| 132 | + |
| 133 | +## Chapter 9: event-driven architecture part 3: a second use case, cancel_shipment -- command handler pattern |
| 134 | + |
| 135 | +now we want to be able to cancel a shipment. maybe a boat sank and all the orders allocated to it need re-allocating. but we don't want to do the reallocation and the cancellation in the same transaction. So a command "cancel shipment" that raises a number of independent "reallocate" commands makes sense |
| 136 | + |
| 137 | + |
| 138 | +We also decide we don't need a web api for this, a command-line interface makes sense. but what's a sensible abstraction that gives use access to our use cases from both the command-line and a flask api? commands. we've been talking about them for a while, time to make them into a real thing. |
| 139 | + |
| 140 | +==> show how commands can be put on the message bus just like events. |
| 141 | + |
| 142 | +code examples / patterns: reuse message bus for commands |
| 143 | + |
| 144 | +related post from existing blog: https://io.made.com/introducing-command-handler/ |
| 145 | + |
| 146 | + |
| 147 | +## Chapter 10: event-driven architecture part 4: reactive microservices |
| 148 | + |
| 149 | +We've got a microservice, but we've so far glossed over how it actually gets data about the outside world -- how does it know about new shipments? |
| 150 | +Show how the event-driven system we've built so far is a great way of integrating between separate applications: our logistics app can emit events about new shipments, and our app can consume them in exactly the same way that it consumes its internal events and commands. |
| 151 | + |
| 152 | +code examples / patterns: events as a microservices integration platform |
| 153 | + |
| 154 | + |
| 155 | + |
| 156 | +## Appendix 1: swapping out flask for asyncio |
| 157 | + |
| 158 | +demonstrate how our layered architecture makes it easy to do infrastructure changes whilst keeping our business logic intact |
| 159 | + |
| 160 | +this could be an exercise for the reader tbh. or a video |
| 161 | + |
| 162 | + |
| 163 | +## Appendix 2: patterns for dependency injection |
| 164 | + |
| 165 | +with + without framework, `@inject`, and bob's crazy, heretical, unclean type-hints based one. |
| 166 | + |
| 167 | +related post from existing blog: https://io.made.com/dependency-injection-with-type-signatures-in-python/ |
62 | 168 |
|
0 commit comments