You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: chapter_01_domain_model.asciidoc
+35-33Lines changed: 35 additions & 33 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -91,7 +91,7 @@ But there's a lot more to DDD and to the processes, tools, and techniques for
91
91
developing a domain model. We hope to give you a taste of it, though,
92
92
and cannot encourage you enough to go on and read a proper DDD book:
93
93
94
-
* The original "blue book,"_Domain-Driven Design_ by Eric Evans (Addison-Wesley Professional)
94
+
* The original "blue book,"_Domain-Driven Design_ by Eric Evans (Addison-Wesley Professional)
95
95
* The "red book," _Implementing Domain-Driven Design_
96
96
by Vaughn Vernon (Addison-Wesley Professional)
97
97
@@ -179,7 +179,6 @@ so that the examples are easier to talk about.
179
179
pass:[<a data-type="xref" href="#allocation_notes" data-xrefstyle="select:nopage">#allocation_notes</a>] shows some notes we might have taken while having a
180
180
conversation with our domain experts about allocation.
181
181
182
-
[role="small"]
183
182
[[allocation_notes]]
184
183
.Some Notes on Allocation
185
184
****
@@ -254,6 +253,7 @@ system, and the names of the classes and variables that we use are taken from th
254
253
business jargon. We could show this code to our nontechnical coworkers, and
255
254
they would agree that this correctly describes the behavior of the system.
256
255
256
+
[role="pagebreak-before"]
257
257
And here is a domain model that meets our requirements:
258
258
259
259
[[domain_model_1]]
@@ -368,7 +368,7 @@ So far, we can manage the implementation by just incrementing and decrementing
368
368
`Batch.available_quantity`, but as we get into `deallocate()` tests, we'll be
369
369
forced into a more intelligent solution:
370
370
371
-
371
+
[role="pagebreak-before"]
372
372
[[test_deallocate_unallocated]]
373
373
.This test is going to require a smarter model (test_batches.py)
Copy file name to clipboardExpand all lines: chapter_02_repository.asciidoc
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -509,7 +509,7 @@ class AbstractRepository(abc.ABC):
509
509
parent class.footnote:[To really reap the benefits of ABCs (such as they
510
510
may be), be running helpers like `pylint` and `mypy`.]
511
511
512
-
<2> `raise NotImplementedError` is nice, but it's neither necessary nor sufficient. Your abstract methods can have real behavior that subclasses
512
+
<2> `raise NotImplementedError` is nice, but it's neither necessary nor sufficient. In fact, your abstract methods can have real behavior that subclasses
513
513
can call out to, if you really want.
514
514
515
515
[role="pagebreak-before less_space"]
@@ -838,7 +838,9 @@ summarize the costs and benefits((("Repository pattern", "and persistence ignora
838
838
We want to be clear that we're not saying every single application needs
839
839
to be built this way; only sometimes does the complexity of the app and domain
840
840
make it worth investing the time and effort in adding these extra layers of
841
-
indirection. With that in mind, <<chapter_02_repository_tradeoffs>> shows
841
+
indirection.
842
+
843
+
With that in mind, <<chapter_02_repository_tradeoffs>> shows
842
844
some of the pros and cons of the Repository pattern and our persistence-ignorant
Copy file name to clipboardExpand all lines: chapter_04_service_layer.asciidoc
+10-12Lines changed: 10 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,16 +23,15 @@ add a Flask API that will talk to the service layer, which will serve as the
23
23
entrypoint to our domain model. Because our service layer depends on the
24
24
`AbstractRepository`, we can unit test it by using `FakeRepository` but run our production code using `SqlAlchemyRepository`.
25
25
26
-
NOTE: In our diagrams, we are using the convention that new components
27
-
are highlighted with bold text/lines (and yellow/orange color, if you're
28
-
reading a digital version).
29
-
30
26
[[maps_service_layer_after]]
31
27
.The service layer will become the main way into our app
32
28
image::images/apwp_0402.png[]
33
29
34
30
// IDEA more detailed legend
35
31
32
+
In our diagrams, we are using the convention that new components
33
+
are highlighted with bold text/lines (and yellow/orange color, if you're
34
+
reading a digital version).
36
35
37
36
[TIP]
38
37
====
@@ -721,6 +720,13 @@ Adding the service layer((("service layer", "benefits of")))((("Flask framework"
721
720
dependencies of our service ((("Flask framework", "Flask API and service layer", "service layer dependencies")))((("service layer", "dependencies of")))((("dependencies", "abstract dependencies of service layer")))layer: the domain model
722
721
and `AbstractRepository` (the port, in ports and adapters terminology).
723
722
723
+
When we run the tests, <<service_layer_diagram_test_dependencies>> shows
724
+
how we implement the abstract dependencies by using `FakeRepository` (the
725
+
adapter).((("service layer", "dependencies of", "testing")))((("dependencies", "abstract dependencies of service layer", "testing")))
726
+
727
+
And when we actually run our app, we swap in the "real" dependency((("service layer", "dependencies of", "real dependencies at runtime")))((("dependencies", "real service layer dependencies at runtime"))) shown in
When we run the tests, <<service_layer_diagram_test_dependencies>> shows
745
-
how we implement the abstract dependencies by using `FakeRepository` (the
746
-
adapter).((("service layer", "dependencies of", "testing")))((("dependencies", "abstract dependencies of service layer", "testing")))
747
-
748
750
[role="width-75"]
749
751
[[service_layer_diagram_test_dependencies]]
750
752
.Tests provide an implementation of the abstract dependency
@@ -774,9 +776,6 @@ image::images/apwp_0404.png[]
774
776
+----------------------+
775
777
----
776
778
777
-
And when we actually run our app, we swap in the "real" dependency((("service layer", "dependencies of", "real dependencies at runtime")))((("dependencies", "real service layer dependencies at runtime"))) shown in
778
-
<<service_layer_diagram_runtime_dependencies>>.
779
-
780
779
[role="width-75"]
781
780
[[service_layer_diagram_runtime_dependencies]]
782
781
.Dependencies at runtime
@@ -857,7 +856,6 @@ a|
857
856
controllers").((("Flask framework", "Flask API and service layer", startref="ix_Flskapp")))((("service layer", startref="ix_serlay")))
858
857
|===
859
858
860
-
[role="pagebreak-before less_space"]
861
859
But there are still some bits of awkwardness to tidy up:
862
860
863
861
* The service layer is still tightly coupled to the domain, because
// IDEA: (EJ3) an example that is overmocked would be good here if you decide to
187
187
// add one. Ch12 already has one that could be expanded.
@@ -334,7 +334,7 @@ TIP: In general, if you find yourself needing to do domain-layer stuff directly
334
334
in your service-layer tests, it may be an indication that your service
335
335
layer is incomplete.
336
336
337
-
337
+
[role="pagebreak-before"]
338
338
And the implementation is just two lines:
339
339
340
340
[[add_batch_service]]
@@ -392,7 +392,7 @@ This is a really nice place to be in. Our service-layer tests depend on only
392
392
the service layer itself, leaving us completely free to refactor the model as
393
393
we see fit.((("test-driven development (TDD)", "fully decoupling service layer from the domain", startref="ix_TDDdecser")))((("domain layer", "fully decoupling service layer from", startref="ix_domlaydec")))((("service layer", "fully decoupling from the domain", startref="ix_serlaydec")))
394
394
395
-
395
+
[role="pagebreak-before less_space"]
396
396
=== Carrying the Improvement Through to the E2E Tests
397
397
398
398
In the same way that adding `add_batch` helped decouple our service-layer
Once you have a service layer in place, you really can move the majority
473
-
of your test coverage to unit tests and develop a healthy test pyramid.((("test-driven development (TDD)", "benefits of service layer to")))((("service layer", "benefits to test-driven development"))) A few
474
-
things will help along the way:
475
-
476
-
* Express your service layer in terms of primitives rather than domain objects.
477
-
478
-
* In an ideal world, you'll have all the services you need to be able to test
479
-
entirely against the service layer, rather than hacking state via
480
-
repositories or the database.((("test-driven development (TDD)", "types of tests, rules of thumb for"))) This pays off in your end-to-end tests as well.
481
-
473
+
of your test coverage to unit tests and develop a healthy test pyramid.((("test-driven development (TDD)", "benefits of service layer to")))((("service layer", "benefits to test-driven development")))
482
474
483
475
[role="nobreakinside less_space"]
484
476
[[types_of_test_rules_of_thumb]]
@@ -515,4 +507,13 @@ Error handling counts as a feature::
* Express your service layer in terms of primitives rather than domain objects.
514
+
515
+
* In an ideal world, you'll have all the services you need to be able to test
516
+
entirely against the service layer, rather than hacking state via
517
+
repositories or the database.((("test-driven development (TDD)", "types of tests, rules of thumb for"))) This pays off in your end-to-end tests as well.
0 commit comments