More on this book
Community
Kindle Notes & Highlights
by
Evans Eric
Started reading
December 20, 2016
The fewer and simpler the associations in the model, the better.
But, while bidirectional associations between ENTITIES may be hard to maintain, bidirectional associations between two VALUE OBJECTS just make no sense.
Complex operations can easily swamp a simple object, obscuring its role. And because these operations often draw together many domain objects, coordinating them and putting them into action, the added responsibility will create dependencies on all those objects, tangling concepts that could be understood independently. Sometimes services masquerade as model objects, appearing as objects with no meaning beyond doing some operation. These “doers” end up with names ending in “Manager” and the like. They have no state of their own nor any meaning in the domain beyond the operation they host.
...more
A SERVICE tends to be named for an activity, rather than an entity—a verb rather than a noun.
But when an operation is actually an important domain concept, a SERVICE forms a natural part of a MODEL-DRIVEN DESIGN. Declared
Statelessness here means that any client can use any instance of a particular SERVICE without regard to the instance’s individual history.
the SERVICE does not hold state of its own that affects its own behavior, as most domain objects do.
It takes care to distinguish SERVICES that belong to the domain layer from those of other layers, and to factor responsibilities to keep that distinction sharp.
Most SERVICES discussed in the literature are purely technical and belong in the infrastructure layer. Domain and application SERVICES collaborate with these infrastructure SERVICES.
Funds transfer has a meaning in the banking domain language, and it involves fundamental business logic. Technical SERVICES should lack any business meaning at all.
We can dress up such external SERVICES with a FACADE that takes inputs in terms of the model, perhaps returning a Funds Transfer object as its result.
Although this pattern discussion has emphasized the expressiveness of modeling a concept as a SERVICE, the pattern is also valuable as a means of controlling granularity in the interfaces of the domain layer, as well as decoupling clients from the ENTITIES and VALUE OBJECTS.
A “doer” object may be satisfactory as an implementation of a SERVICE’s interface.
There are technical considerations, but cognitive overload is the primary motivation for modularity.
The MODULES in the domain layer should emerge as a meaningful part of the model, telling the story of the domain on a larger scale.
Yet it isn’t just code being divided into MODULES, but concepts.
limit to how many things a person can think about at once (hence low coupling). Incoherent fragments of ideas are as hard to understand as an undifferentiated soup of ideas (hence high cohesion).
Whenever two model elements are separated into different modules, the relationships between them become less direct than they were, which increases the overhead of understanding their place in the design. Low
This high cohesion of objects with related responsibilities allows modeling and design work to concentrate within a single MODULE, a scale of complexity a human mind can easily handle.
Letting the MODULES reflect changing understanding of the domain will also allow more freedom for the objects within them to evolve.
Choose MODULES that tell the story of the system and contain a cohesive set of concepts.
Give the MODULES names that become part of the UBIQUITOUS LANGUAGE.
should reflect insight into the domain.
Developers can handle these problems if they understand the story the model is telling them.
we need to look for ways of minimizing the work of refactoring MODULES, and minimizing clutter in communicating to other developers.
In Java, unfortunately, there is no escape from importing into individual classes, but you can at least import entire packages at a time,
If the framework’s partitioning conventions pull apart the elements implementing the conceptual objects, the code no longer reveals the model.
There is only so much partitioning a mind can stitch back together, and if the framework uses it all up, the domain developers lose their ability to chunk the model into meaningful pieces.
Unless there is a real intention to distribute code on different servers, keep all the code that implements a single conceptual object in the same MODULE, if not the same object.
“high cohesion/low coupling.”
Use packaging to separate the domain layer from other code. Otherwise, leave as much freedom as possible to the domain developers to package the domain objects in ways that support their model and design choices.
One exception arises when code is generated based on a declarative design (discussed in Chapter 10). In that case, the developers do not need to read the code, and it is better to put it into a separate package so that it is out of the way, not cluttering up the design elements developers actually have to work with.
A class diagram representing a model of the shipping domain
SPECIFICATION pattern
The diagram is telling the reader that there is a SPECIFICATION of delivery, and the details of that are not important to think about (and, in fact, could be easily changed later).
When software doesn’t have a clean design, developers dread even looking at the existing mess, much less making a change that could aggravate the tangle or break something through an unforeseen dependency.
In any but the smallest systems, this fragility places a ceiling on the richness of behavior it is feasible to build. It stops refactoring and iterative refinement.
will clue the reader in enough to get started using the class, especially with the example the test provides.
INTENTION-REVEALING INTERFACES
As soon as this arbitrarily deep nesting is involved, it becomes very hard to anticipate all the consequences of invoking an operation. The developer of the client may not have intended the effects of the second-tier and third-tier operations—they’ve become side effects in every sense of the phrase.
Interactions of multiple rules or compositions of calculations become extremely difficult to predict. The developer calling an operation must understand its implementation and the implementation of all its delegations in order to anticipate the result. The usefulness of any abstraction of interfaces is limited if the developers are forced to pierce the veil. Without safely predictable abstractions, the developers must limit the combinatory explosion, placing a low ceiling on the richness of behavior that is feasible to build.
First, you can keep the commands and queries strictly segregated in different operations. Ensure that the methods that cause changes do not return domain data and are kept as simple as possible.
Perform all queries and calculations in methods that cause no observable side effects (Meyer 1988).
A VALUE OBJECT can be created in answer to a query, handed off, and forgotten—unlike an ENTITY, whose life cycle is carefully regulated.
VALUE OBJECTS are immutable, which implies that, apart from initializers called only during creation, all their operations are functions.
VALUE OBJECTS, like functions, are safer to use an...
This highlight has been truncated due to consecutive passage length restrictions.
An operation that mixes logic or calculations with state change should be refactored in...
This highlight has been truncated due to consecutive passage length restrictions.
move the responsibility for the complex calculations into a VALUE OBJECT.
Place as much of the logic of the program as possible into functions, operations that return results with no observable side effects. Strictly segregate commands (methods that result in modifications to observable state) into very simple operations that do not return domain information. Further control side effects by moving complex logic into VALUE OBJECTS when a concept fitting the responsibility presents itself.
When a FUNCTION is presented through an INTENTION-REVEALING INTERFACE, a developer can use it without understanding the detail of its implementation.

