Skip to content

Commit 02a837d

Browse files
committed
Merge remote-tracking branch 'atlas/master' into indexing
2 parents 094b48b + 047e710 commit 02a837d

18 files changed

+62
-62
lines changed

appendix_csvs.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ And here's((("Unit of Work pattern", "UoW for CSVs"))) what a UoW for CSVs would
240240

241241

242242
[[csvs_uow]]
243-
.A UoW for CSVs: commit = csv.writer. (src/allocation/service_layer/csv_uow.py)
243+
.A UoW for CSVs: commit = csv.writer (src/allocation/service_layer/csv_uow.py)
244244
====
245245
[source,python]
246246
----

appendix_django.asciidoc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,12 @@ but it is still made up of familiar-looking Django code:
104104
def test_repository_can_retrieve_a_batch_with_allocations():
105105
sku = "PONY-STATUE"
106106
d_line = django_models.OrderLine.objects.create(orderid="order1", sku=sku, qty=12)
107-
d_b1 = django_models.Batch.objects.create(reference="batch1", sku=sku, qty=100, eta=None)
108-
d_b2 = django_models.Batch.objects.create(reference="batch2", sku=sku, qty=100, eta=None)
107+
d_b1 = django_models.Batch.objects.create(
108+
reference="batch1", sku=sku, qty=100, eta=None
109+
)
110+
d_b2 = django_models.Batch.objects.create(
111+
reference="batch2", sku=sku, qty=100, eta=None
112+
)
109113
django_models.Allocation.objects.create(line=d_line, batch=d_batch1)
110114
111115
repo = repository.DjangoRepository()

appendix_validation.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ bear in mind that invalid data wandering through your system is a time bomb;
230230
the deeper it gets, the more damage it can do, and the fewer tools
231231
you have to respond to it.
232232

233-
Back in <<chapter_06_uow>>, we said that the message bus was a great place to put
233+
Back in <<chapter_08_events_and_message_bus>>, we said that the message bus was a great place to put
234234
cross-cutting concerns, and validation is a perfect example of that. Here's how
235235
we might change our bus to perform validation for us:
236236

chapter_01_domain_model.asciidoc

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ too late, and OO enthusiasts will tell you to look further back to Ivar
7575
Jacobson and Grady Booch; the term has been around since the mid-1980s.]
7676
and it's been a hugely successful movement in transforming the way people
7777
design software by focusing on the core business domain. Many of the
78-
architecture patterns that we cover in this book—including Entity, Aggregate, Value Object (see <<chapter_06_uow>>), and Repository (in
78+
architecture patterns that we cover in this book—including Entity, Aggregate, Value Object (see <<chapter_07_aggregate>>), and Repository (in
7979
<<chapter_02_repository,the next chapter>>)—come from the DDD tradition.
8080
8181
In a nutshell, DDD says that the most important thing about software is that it
@@ -212,6 +212,11 @@ We can't allocate the same line twice. For example:
212212
Batches have an _ETA_ if they are currently shipping, or they may be in _warehouse stock_. We allocate to warehouse stock in preference to shipment batches. We allocate to shipment batches in order of which has the earliest ETA.
213213
****
214214

215+
=== Unit Testing Domain Models
216+
217+
We're not going to show you how TDD works in this book, but we want to show you
218+
how we would construct a model from this business conversation.((("unit testing", "of domain models", id="ix_UTDM")))((("domain modeling", "unit testing domain models", id="ix_dommodUT")))
219+
215220
[role="nobreakinside less_space"]
216221
.Exercise for the Reader
217222
******************************************************************************
@@ -227,12 +232,6 @@ scratch, or combine/rewrite them however you like.
227232
228233
******************************************************************************
229234

230-
231-
=== Unit Testing Domain Models
232-
233-
We're not going to show you how TDD works in this book, but we want to show you
234-
how we would construct a model from this business conversation.((("unit testing", "of domain models", id="ix_UTDM")))((("domain modeling", "unit testing domain models", id="ix_dommodUT")))
235-
236235
Here's what one of our first tests might look like:
237236

238237
[[first_test]]
@@ -970,7 +969,7 @@ Not everything has to be an object::
970969
or `get_baz()` waiting to happen.((("functions")))
971970
972971
This is the time to apply your best OO design principles::
973-
Revisit the ((("object-oriented design principles")))SOLID principles and all the other good heuristics like "a versus is-a,"
972+
Revisit the ((("object-oriented design principles")))SOLID principles and all the other good heuristics like "has a versus is-a,"
974973
"prefer composition over inheritance," and so on.
975974
976975
You'll((("domain modeling", startref="ix_dommod"))) also want to think about consistency boundaries and aggregates::

chapter_02_repository.asciidoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ We'll need a way to retrieve batch info from the database and instantiate our do
8686
model objects from it, and we'll also need a way of saving them back to the
8787
database.
8888

89-
_(What? Oh, "gubbins" is a British word for "stuff." You can just ignore that. It's pseudocode, OK?)_
89+
_What? Oh, "gubbins" is a British word for "stuff." You can just ignore that. It's pseudocode, OK?_
9090

9191

9292
=== Applying the DIP to Data Access
@@ -269,7 +269,7 @@ inversion and the Repository pattern to Django.
269269
==== Inverting the Dependency: ORM Depends on Model
270270

271271
Well, thankfully, that's not the only way to use SQLAlchemy.((("object-relational mappers (ORMs)", "ORM depends on the data model")))((("domain model", "translating to relational database", "ORM depends on the model")))((("dependency inversion principle", "ORM depends on the data model")))((("SQLAlchemy", "explicit ORM mapping with SQLAlchemy Table objects")))((("classical mapping")))((("mappers"))) The alternative is
272-
to define your schema separately, and an explicit _mapper_ for how to convert
272+
to define your schema separately, and to define an explicit _mapper_ for how to convert
273273
between the schema and our domain model, what SQLAlchemy calls a
274274
https://oreil.ly/ZucTG[classical mapping]:
275275

@@ -666,9 +666,9 @@ def test_repository_can_retrieve_a_batch_with_allocations(session):
666666
assert retrieved == expected # Batch.__eq__ only compares reference #<3>
667667
assert retrieved.sku == expected.sku #<4>
668668
assert retrieved._purchased_quantity == expected._purchased_quantity
669-
assert retrieved._allocations == {
669+
assert retrieved._allocations == { #<4>
670670
model.OrderLine("order1", "GENERIC-SOFA", 12),
671-
} #<4>
671+
}
672672
----
673673
====
674674

chapter_03_abstractions.asciidoc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -668,9 +668,7 @@ Steve Freeman has a great example of overmocked tests in his talk
668668
https://oreil.ly/jAmtr["Test-Driven Development"].
669669
You should also check out this PyCon talk, https://oreil.ly/s3e05["Mocking and Patching Pitfalls"],
670670
by our esteemed tech reviewer, Ed Jung, which also addresses mocking and its
671-
alternatives.((("&quot;Test-Driven Development: That&#x27;s Not What We Meant&quot;", primary-sortas="Test-Driven Development")))((("Freeman, Steve")))((("PyCon talk on Mocking Pitfalls")))((("Jung, Ed")))
672-
673-
And while we're recommending talks, don't miss Brandon Rhodes talking about
671+
alternatives.((("&quot;Test-Driven Development: That&#x27;s Not What We Meant&quot;", primary-sortas="Test-Driven Development")))((("Freeman, Steve")))((("PyCon talk on Mocking Pitfalls")))((("Jung, Ed"))) And while we're recommending talks, don't miss Brandon Rhodes talking about
674672
https://oreil.ly/oiXJM["Hoisting Your I/O"],
675673
which really nicely covers the issues we're talking about, using another simple example.((("hoisting I/O")))((("Rhodes, Brandon")))
676674

chapter_04_service_layer.asciidoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ business logic, and interfacing code, and we introduce the _Service Layer_
1313
pattern to take care of orchestrating our workflows and defining the use
1414
cases of our system.
1515

16-
We'll also discuss testing: by combining the Service Layer with our Repository
16+
We'll also discuss testing: by combining the Service Layer with our repository
1717
abstraction over the database, we're able to write fast tests, not just of
1818
our domain model but of the entire workflow for a use case.
1919

2020
<<maps_service_layer_after>> shows what we're aiming for: we're going to
2121
add a Flask API that will talk to the service layer, which will serve as the
2222
entrypoint to our domain model. Because our service layer depends on the
23-
`AbstractRepository`, we can unit test it by using `FakeRepository` but run it our production code using `SqlAlchemyRepository`.
23+
`AbstractRepository`, we can unit test it by using `FakeRepository` but run our production code using `SqlAlchemyRepository`.
2424

2525
NOTE: In our diagrams, we are using the convention that new components
2626
are highlighted with bold text/lines (and yellow/orange color, if you're
@@ -42,7 +42,7 @@ chapter_04_service_layer branch https://oreil.ly/TBRuy[on GitHub]:
4242
git clone https://github.com/cosmicpython/code.git
4343
cd code
4444
git checkout chapter_04_service_layer
45-
# or to code along, checkout the previous chapter:
45+
# or to code along, checkout Chapter 2:
4646
git checkout chapter_02_repository
4747
----
4848
====
@@ -669,7 +669,7 @@ Here's one way we could organize things:
669669
<1> Let's have a folder for our domain model.((("domain model", "folder for"))) Currently that's just one file,
670670
but for a more complex application, you might have one file per class; you
671671
might have helper parent classes for `Entity`, `ValueObject`, and
672-
`Aggregate`, you might add an __exceptions.py__ for domain-layer exceptions
672+
`Aggregate`, and you might add an __exceptions.py__ for domain-layer exceptions
673673
and, as you'll see in <<part2>>, pass:[<span class="keep-together"><em>commands.py</em></span>] and __events.py__.
674674

675675
<2> We'll distinguish the service layer. Currently that's just one file

chapter_05_high_gear_low_gear.asciidoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ NOTE: Are you thinking to yourself, POST to _/add_batch_? That's not
428428
very RESTful! You're quite right. We're being happily sloppy, but
429429
if you'd like to make it all more RESTy, maybe a POST to _/batches_,
430430
then knock yourself out! Because Flask is a thin adapter, it'll be
431-
easy. See the next sidebar.
431+
easy. See <<types_of_test_rules_of_thumb, the next sidebar>>.
432432

433433
And our hardcoded SQL queries from _conftest.py_ get replaced with some
434434
API calls, meaning the API tests have no dependencies other than the API,
@@ -515,3 +515,5 @@ Error handling counts as a feature::
515515
unit tests, of course).((("test-driven development (TDD)", startref="ix_TDD")))
516516
517517
******************************************************************************
518+
519+
Onto the next chapter!

chapter_06_uow.asciidoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ chapter_06_uow branch https://oreil.ly/MoWdZ[on GitHub]:
2222
git clone https://github.com/cosmicpython/code.git
2323
cd code
2424
git checkout chapter_06_uow
25-
# or to code along, check out the previous chapter:
25+
# or to code along, checkout Chapter 4:
2626
git checkout chapter_04_service_layer
2727
----
2828
====
@@ -31,7 +31,7 @@ git checkout chapter_04_service_layer
3131
.Without UoW: API talks directly to three layers
3232
image::images/apwp_0601.png[]
3333

34-
<<after_uow_diagram>> shows our target state: the Flask API now does only two
34+
<<after_uow_diagram>> shows our target state. The Flask API now does only two
3535
things: it initializes a unit of work, and it invokes a service. The service
3636
collaborates with the UoW (we like to think of the UoW as being part of the
3737
service layer), but neither the service function itself nor Flask now needs

chapter_07_aggregate.asciidoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ ____
185185

186186
Per Evans, our aggregate has a root entity (the Cart) that encapsulates access
187187
to items. Each item has its own identity, but other parts of the system will always
188-
refer to the cart only as an indivisible whole.
188+
refer to the Cart only as an indivisible whole.
189189

190190
TIP: Just as we sometimes use pass:[<code><em>_leading_underscores</em></code>] to mark methods or functions
191191
as "private," you can think of aggregates as being the "public" classes of our
@@ -655,7 +655,7 @@ There are essentially three options((("version numbers", "implementation options
655655
1. `version_number` lives in the domain; we add it to the `Product` constructor,
656656
and `Product.allocate()` is responsible for incrementing it.
657657

658-
2. The services layer could do it! The version number isn't _strictly_ a domain
658+
2. The service layer could do it! The version number isn't _strictly_ a domain
659659
concern, so instead our service layer could assume that the current version number
660660
is attached to `Product` by the repository, and the service layer will increment it
661661
before it does the `commit()`.
@@ -882,7 +882,7 @@ performing some performance experiments.((("testing", "for data integrity rules"
882882

883883
Specific choices around concurrency control vary a lot based on business
884884
circumstances and storage technology choices, but we'd like to bring this
885-
chapter back to the((("aggregates", "and consistency boundaries recap"))) conceptual idea of an Aggregate: we explicitly model an
885+
chapter back to the((("aggregates", "and consistency boundaries recap"))) conceptual idea of an aggregate: we explicitly model an
886886
object as being the main entrypoint to some subset of our model, and as being in
887887
charge of enforcing the invariants and business rules that apply across all of
888888
those objects.
@@ -987,7 +987,7 @@ models that represent the shared language of your business experts. Hurrah!
987987
NOTE: At the risk of laboring the point--we've been at pains to point out that
988988
each pattern comes at a cost.((("patterns, deciding whether you need to use them"))) Each layer of indirection has a price in terms
989989
of complexity and duplication in our code and will be confusing to programmers
990-
who've never seen them before. If your app is essentially a simple CRUD
990+
who've never seen these patterns before. If your app is essentially a simple CRUD
991991
wrapper around a database and isn't likely to be anything more than that
992992
in the foreseeable future, _you don't need these patterns_. Go ahead and
993993
use Django, and save yourself a lot of bother.((("CRUD wrapper around a database")))

0 commit comments

Comments
 (0)