Skip to content

Commit d959a7e

Browse files
committed
latest outline
1 parent f4c9883 commit d959a7e

1 file changed

Lines changed: 139 additions & 33 deletions

File tree

outline.md

Lines changed: 139 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,168 @@
1-
Proposed Book Title:
2-
Proposed Book Subtitle:
1+
# Outline
32

4-
* The Clean Architecture in Python
5-
* Ports and Adapters with Python
6-
* Enterprise Design Patterns in Python
3+
## Preface/Intro
74

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)
87

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).
99
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.
1511
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.
1813
14+
An overview of the central argument:
1915

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.
2117
2218

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?
2420

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?
2522

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.
2824

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)
3028

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.
3230

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/
3532

3633

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.
3834

35+
## Chapter 2: persistence and the repository pattern
3936

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.
4138

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.
4440

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)
4642

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
4945

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
5247

48+
related post from existing blog: https://io.made.com/repository-and-unit-of-work-pattern-in-python/
5349

5450

55-
Book Outline (chapter level is fine):
5651

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
5753

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?
6055

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/
62168

0 commit comments

Comments
 (0)