Skip to content

Commit

Permalink
Merge branch 'master' into moredjangoadvice
Browse files Browse the repository at this point in the history
  • Loading branch information
hjwp committed Jan 14, 2020
2 parents fba3444 + b401adf commit 92c3e87
Show file tree
Hide file tree
Showing 29 changed files with 1,090 additions and 859 deletions.
18 changes: 10 additions & 8 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ In the meantime, pull requests, typofixes, and more substantial feedback + sugge
| [Chapter 2: Repository](chapter_02_repository.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_02_repository)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 3: Interlude: Abstractions](chapter_03_abstractions.asciidoc) | |
| [Chapter 4: Service Layer (and Flask API)](chapter_04_service_layer.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_04_service_layer)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 5: Unit of Work](chapter_05_uow.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_05_uow)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 6: Aggregates](chapter_06_aggregate.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_06_aggregate)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 5: TDD in High Gear and Low Gear](chapter_05_high_gear_low_gear.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_05_high_gear_low_gear)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 6: Unit of Work](chapter_06_uow.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_06_uow)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 7: Aggregates](chapter_07_aggregate.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_07_aggregate)](https://travis-ci.org/cosmicpython/code) |
| [**Part 2 Intro**](part2.asciidoc) | |
| [Chapter 7: Domain Events and a Simple Message Bus](chapter_07_events_and_message_bus.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_07_events_and_message_bus)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 8: Going to Town on the MessageBus](chapter_08_all_messagebus.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_08_all_messagebus)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 9: Commands](chapter_09_commands.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_09_commands)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 10: External Events for Integration](chapter_10_external_events.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_10_external_events)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 11: CQRS](chapter_11_cqrs.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_11_cqrs)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 12: Dependency Injection](chapter_12_dependency_injection.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_12_dependency_injection)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 8: Domain Events and a Simple Message Bus](chapter_08_events_and_message_bus.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_08_events_and_message_bus)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 9: Going to Town on the MessageBus](chapter_09_all_messagebus.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_09_all_messagebus)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 10: Commands](chapter_10_commands.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_10_commands)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 11: External Events for Integration](chapter_11_external_events.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_11_external_events)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 12: CQRS](chapter_12_cqrs.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_12_cqrs)](https://travis-ci.org/cosmicpython/code) |
| [Chapter 13: Dependency Injection](chapter_13_dependency_injection.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=chapter_13_dependency_injection)](https://travis-ci.org/cosmicpython/code) |
| [Epilogue: How do I get there from here?](epilogue_1_how_to_get_there_from_here.asciidoc) | |
| [Appendix A: Recap table](appendix_ds1_table.asciidoc) | |
| [Appendix B: Project Structure](appendix_project_structure.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=appendix_project_structure)](https://travis-ci.org/cosmicpython/code) |
| [Appendix C: A major infrastructure change, made easy](appendix_csvs.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=appendix_csvs)](https://travis-ci.org/cosmicpython/code) |
| [Appendix D: Django](appendix_django.asciidoc) | [![Build Status](https://travis-ci.org/cosmicpython/code.svg?branch=appendix_django)](https://travis-ci.org/cosmicpython/code) |
Expand Down
2 changes: 1 addition & 1 deletion appendix_csvs.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This appendix is intended as a little illustration of the benefits of the
Repository, Unit of Work, and Service Layer patterns. It's intended to
follow on from <<chapter_05_uow>>.
follow on from <<chapter_06_uow>>.

Just as we finish building out our Flask API and getting it ready for release,
the business come to us apologetically saying they're not ready to use our API
Expand Down
58 changes: 58 additions & 0 deletions appendix_ds1_table.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
[[appendix_ds1_table]]
[appendix]
== Summary diagram and table

[[recap_diagram]]
.Our architecture by the end of the book
image::images/maps_chapter_12_after.png[]

// TODO: make a version without the yellow bits


[[ds1_table]]
.The components of our architecture and what they all do.
[cols="3"]
|===
| Layer | Component | Description

.5+a| Domain

* Defines the business logic.


| Entity | A domain object whose attributes may change, but that has a recognizable identity over time.

| Value Object | An immutable domain object whose attributes entirely define it. It is fungible with other identical objects.

| Aggregate | Cluster of associated objects that we treat as a unit for the purpose of data changes.

| Event | Represents something that happened, emitted by or stored on domain objects.

| Command | Represents a job the system should perform.

.3+a| Service layer

* Defines the jobs the system should perform and orchestrates different components.

| Command/Event Handler | Receives a Command or Event and performs what needs to happen.
| Unit of Work | Abstraction around data integrity. Each unit of work represents an atomic update.
| Message Bus (Internal) | Handles Commands and Events by routing them to the appropriate Handler.

.2+a| Adapters (Secondary)

* Concrete implementations of an interface that goes from our system out
to the outside world / I/O

| Repository | Abstraction around persistent storage. Each Aggregate has its own Repository.
| Event Publisher | Pushes Events onto the external message bus.

.2+a| Entrypoints (Primary adapters)

* Translate external inputs into calls into the service layer.

| Web | Receives web requests and translates them into Commands, passing them to the Internal Message Bus.
| Event Consumer | Reads Events from the External Message Bus and translates them into Commands, passing them to the Internal Message Bus.

| n/a | External Message Bus (Message Broker) | A piece of infrastructure that different services use to intercommunicate, via Events.
|===

2 changes: 1 addition & 1 deletion appendix_project_structure.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ at if you get tired of hand-rolling your own environment-based config functions.
TIP: Don't let this config module become a dumping ground full of things that
are only vaguely related to config, and is then imported all over the place.
Keep things immutable and only modify them via environment variables.
If you decide to use a <<chapter_12_dependency_injection,bootstrap script>>,
If you decide to use a <<chapter_13_dependency_injection,bootstrap script>>,
you can make it the only place (other than tests) that config is imported.

=== Docker-Compose and Containers Config
Expand Down
15 changes: 8 additions & 7 deletions appendix_validation.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
== Validation

Whenever we're teaching and talking about these techniques, one question that
comes up over and over is "where should I do validation? Does that belong with
comes up over and over is "Where should I do validation? Does that belong with
my business logic, in the domain model, or is that an infrastructural concern?"

As with any architectural question, the answer is: it depends!
Expand Down Expand Up @@ -91,7 +91,7 @@ declaration of our message types.


[[command_factory]]
.A command factory with schema (src/allocation/commdands.py)
.A command factory with schema (src/allocation/commands.py)
====
[source,python]
----
Expand Down Expand Up @@ -159,7 +159,7 @@ schema, or to build libraries that validate incoming messages and share them
among systems. This likewise fails the robustness test.
Let's imagine, for example, that the procurement system adds new fields to the
`change_batch_quantity` message that record the reason for the change, and the
`ChangeBatchQuantity` message that record the reason for the change, and the
email of the user responsible for the change.
Since these fields don't matter to the allocation service, we should simply
Expand Down Expand Up @@ -289,7 +289,7 @@ teapot to order five", but it's meaningless.
----
====

We can read this json blob as an Allocate command but we can't successfully
We can read this json blob as an `Allocate` command but we can't successfully
execute it, because it's _nonsense_.

We tend to validate semantic concerns at the message handler layer with a kind
Expand Down Expand Up @@ -430,9 +430,10 @@ SCARCE-CLOCK to order 76543", the message is *syntactically* valid, and
*semantically* valid, but we're unable to comply because we don't have the stock
available.


TODO: more here, but basically the message is: we validate pragmatics in the
model layer, yes?
TIP: Once you've validated the syntax and semantics of your commands
at the edges of your system, the domain is the place for the rest
of your validation. Validation of pragmatics is often a core part
of your business rules.



Expand Down
18 changes: 10 additions & 8 deletions atlas.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@
"chapter_02_repository.asciidoc",
"chapter_03_abstractions.asciidoc",
"chapter_04_service_layer.asciidoc",
"chapter_05_uow.asciidoc",
"chapter_06_aggregate.asciidoc",
"chapter_05_high_gear_low_gear.asciidoc",
"chapter_06_uow.asciidoc",
"chapter_07_aggregate.asciidoc",

"part2.asciidoc",
"chapter_07_events_and_message_bus.asciidoc",
"chapter_08_all_messagebus.asciidoc",
"chapter_09_commands.asciidoc",
"chapter_10_external_events.asciidoc",
"chapter_11_cqrs.asciidoc",
"chapter_12_dependency_injection.asciidoc",
"chapter_08_events_and_message_bus.asciidoc",
"chapter_09_all_messagebus.asciidoc",
"chapter_10_commands.asciidoc",
"chapter_11_external_events.asciidoc",
"chapter_12_cqrs.asciidoc",
"chapter_13_dependency_injection.asciidoc",

"epilogue_1_how_to_get_there_from_here.asciidoc",

"appendix_ds1_table.asciidoc",
"appendix_project_structure.asciidoc",
"appendix_csvs.asciidoc",
"appendix_django.asciidoc",
Expand Down
31 changes: 19 additions & 12 deletions book.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
:source-highlighter: pygments
:icons: font

= Pythonic Application Architecture Patterns
= Architecture Patterns with Python
:toc:


Expand All @@ -21,33 +21,40 @@ include::chapter_01_domain_model.asciidoc[]

include::chapter_02_repository.asciidoc[]

:sectnums!:

include::chapter_03_abstractions.asciidoc[]

:sectnums:

include::chapter_04_service_layer.asciidoc[]

include::chapter_05_uow.asciidoc[]
include::chapter_05_high_gear_low_gear.asciidoc[]

include::chapter_06_aggregate.asciidoc[]
include::chapter_06_uow.asciidoc[]

include::chapter_07_aggregate.asciidoc[]


include::part2.asciidoc[]

include::chapter_07_events_and_message_bus.asciidoc[]
include::chapter_08_events_and_message_bus.asciidoc[]

include::chapter_09_all_messagebus.asciidoc[]

include::chapter_08_all_messagebus.asciidoc[]
include::chapter_10_commands.asciidoc[]

include::chapter_09_commands.asciidoc[]
include::chapter_11_external_events.asciidoc[]

include::chapter_10_external_events.asciidoc[]
include::chapter_12_cqrs.asciidoc[]

include::chapter_11_cqrs.asciidoc[]
include::chapter_13_dependency_injection.asciidoc[]

include::chapter_12_dependency_injection.asciidoc[]

:sectnums!:

include::epilogue_1_how_to_get_there_from_here.asciidoc[]

:sectnums:

include::appendix_ds1_table.asciidoc[]

include::appendix_project_structure.asciidoc[]

Expand Down
4 changes: 2 additions & 2 deletions chapter_01_domain_model.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Jacobson and Grady Booch; the term has been around since the mid-1980s.]
and it's been a hugely successful movement in transforming the way people
design software by focusing on the core business domain. Many of the
architecture patterns that we cover in this book, like Entity, Aggregate,
and Value Objects (see <<chapter_05_uow>>), and Repository pattern (in
and Value Objects (see <<chapter_06_uow>>), and Repository pattern (in
<<chapter_02_repository,the next chapter>>), all come from the DDD tradition.
In a nutshell, DDD says that the most important thing about software is that it
Expand Down Expand Up @@ -1012,6 +1012,6 @@ This is the time to apply your best OO design principles::
"prefer composition over inheritance", and so on.
You'll also want to think about consistency boundaries and Aggregates::
But that's a topic for <<chapter_06_aggregate>>.
But that's a topic for <<chapter_07_aggregate>>.
*****************************************************************
4 changes: 2 additions & 2 deletions chapter_02_repository.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ repository, and `get` to return a previously added item.footnote:[
You may be thinking, what about `list` or `delete` or `update`, but in the
ideal world, we only modify our model objects one at a time, and delete is
usually handled as a soft-delete, ie `batch.cancel()`. Finally, update is
taken care of by the Unit of Work, as we'll see in <<chapter_05_uow>>.]
taken care of by the Unit of Work, as we'll see in <<chapter_06_uow>>.]
We stick rigidly to using these methods for data access in our domain and our
service layer. This self-imposed simplicity stops us from coupling our domain
model to the database.
Expand Down Expand Up @@ -631,7 +631,7 @@ def test_repository_can_save_a_batch(session):
<2> We keep the `.commit()` outside of the repository, and make
it the responsibility of the caller. There are pros and cons for
this, some of our reasons will become clearer when we get to
<<chapter_05_uow>>.
<<chapter_06_uow>>.

<3> And we use the raw SQL to verify that the right data has been saved.

Expand Down
6 changes: 3 additions & 3 deletions chapter_03_abstractions.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,10 @@ Instead, we like to clearly identify the responsibilities in our codebase, and t
separate those responsibilities out into small, focused objects that are easy to
replace with a test double.

NOTE: You can see an example in <<chapter_07_events_and_message_bus>>
NOTE: You can see an example in <<chapter_08_events_and_message_bus>>
where we `mock.patch()` out an email-sending module, but eventually we
replace that with an explicit bit of dependency injection, in
<<chapter_12_dependency_injection>>.
<<chapter_13_dependency_injection>>.

There are three closely related reasons for our preference:

Expand Down Expand Up @@ -688,7 +688,7 @@ so that's our functional core. The service layer that we build around it
and we use dependency injection to provide those services with stateful
components, so we can still unit test them.
See <<chapter_12_dependency_injection>> for more exploration of making our
See <<chapter_13_dependency_injection>> for more exploration of making our
dependency injection more explicit and centralized.
******************************************************************************

Expand Down
Loading

0 comments on commit 92c3e87

Please sign in to comment.