Domain-Driven Design: Tackling Complexity in the Heart of Software
Rate it:
Open Preview
15%
Flag icon
If the people who write the code do not feel responsible for the model, or don’t understand how to make the model work for an application, then the model has nothing to do with the software.
15%
Flag icon
Any technical person contributing to the model must spend some time touching the code, whatever primary role he or she plays on the project. Anyone responsible for changing code must learn to express a model through the code. Every developer must be involved in some level of discussion about the model and have contact with domain experts. Those who contribute in different ways must consciously engage those who touch the code in a dynamic exchange of model ideas through the UBIQUITOUS LANGUAGE.
15%
Flag icon
This book is not an introduction to object-oriented design, nor does it propose radical design fundamentals.
16%
Flag icon
The design style in this book largely follows the principle of “responsibility-driven design,” put forward in Wirfs-Brock et al. 1990 and updated in Wirfs-Brock 2003. It also draws heavily (especially in Part III) on the ideas of “design by contract” described in Meyer 1988. It is consistent with the general background of other widely held best practices of object-oriented design, which are described in such books as Larman 1998.
16%
Flag icon
The essential principle is that any element of a layer depends only on other elements in the same layer or on elements of the layers “beneath” it.
16%
Flag icon
Isolated layers are much less expensive to maintain, because they tend to evolve at different rates and respond to different needs.
18%
Flag icon
Just using a flexible language doesn’t create a flexible system, but it may well produce an expensive one.
18%
Flag icon
If the architecture isolates the domain-related code in a way that allows a cohesive domain design loosely coupled to the rest of the system, then that architecture can probably support domain-driven design.
18%
Flag icon
Then there are those aspects of the domain that are more clearly expressed as actions or operations, rather than as objects. Although it is a slight departure from object-oriented modeling tradition, it is often best to express these as SERVICES, rather than forcing responsibility for an operation onto some ENTITY or VALUE OBJECT.
18%
Flag icon
They emerge in the domain also, when some activity is modeled that corresponds to something the software must do, but does not correspond with state.
18%
Flag icon
For every traversable association in the model, there is a mechanism in the software with the same properties.
18%
Flag icon
It is important to constrain relationships as much as possible. A bidirectional association means that both objects can be understood only together.
21%
Flag icon
Tracking the identity of ENTITIES is essential, but attaching identity to other objects can hurt system performance, add analytical work, and muddle the model by making all objects look the same.
21%
Flag icon
An object that represents a descriptive aspect of the domain with no conceptual identity is called a VALUE OBJECT.
21%
Flag icon
VALUE OBJECTS are instantiated to represent elements of the design that we care about only for what they are, not who or which they are.
21%
Flag icon
When you care only about the attributes of an element of the model, classify it as a VALUE OBJECT. Make it express the meaning of the attributes it conveys and give it related functionality. Treat the VALUE OBJECT as immutable. Don’t give it any identity and avoid the design complexities necessary to maintain ENTITIES.
22%
Flag icon
Explicitly defining the essential constraints lets developers tweak the design while keeping safe from changing meaningful behavior.
22%
Flag icon
Now, the more common mistake is to give up too easily on fitting the behavior into an appropriate object, gradually slipping toward procedural programming. But when we force an operation into an object that doesn’t fit the object’s definition, the object loses its conceptual clarity and becomes hard to understand or refactor.
22%
Flag icon
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.
22%
Flag icon
Some concepts from the domain aren’t natural to model as objects. Forcing the required domain functionality to be the responsibility of an ENTITY or VALUE either distorts the definition of a model-based object or adds meaningless artificial objects.
22%
Flag icon
A SERVICE tends to be named for an activity, rather than an entity—a verb rather than a noun.
22%
Flag icon
Operation names should come from the UBIQUITOUS LANGUAGE or be introduced into it. Parameters and results should be domain objects.
22%
Flag icon
SERVICES should be used judiciously and not allowed to strip the ENTITIES and VALUE OBJECTS of all their behavior.
22%
Flag icon
A good SERVICE has three characteristics. 1. The operation relates to a domain concept that is not a natural part of an ENTITY or VALUE OBJECT. 2. The interface is defined in terms of other elements of the domain model. 3. The operation is stateless.
22%
Flag icon
Most SERVICES discussed in the literature are purely technical and belong in the infrastructure layer. Domain and application SERVICES collaborate with these infrastructure SERVICES.
23%
Flag icon
in most development systems, it is awkward to make a direct interface between a domain object and external resources. 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. But whatever intermediaries we might have, and even though they don’t belong to us, those SERVICES are carrying out the domain responsibility of funds transfer.
23%
Flag icon
There is a 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).
23%
Flag icon
Like everything else in a domain-driven design, MODULES are a communications mechanism. The meaning of the objects being partitioned needs to drive the choice of MODULES. When you place some classes together in a MODULE, you are telling the next developer who looks at your design to think about them together. If your model is telling a story, the MODULES are chapters.
23%
Flag icon
model-focused thinking produces a deeper solution, rather than an incidental one. And when there has to be a trade-off, it is best to go with the conceptual clarity, even if it means more references between MODULES or occasional ripple effects when changes are made to a MODULE.
24%
Flag icon
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.
24%
Flag icon
Choose a minimum of technical partitioning rules that are essential to the technical environment or actually aid development. For example, decoupling complicated data persistence code from the behavioral aspects of the objects may make refactoring easier.
24%
Flag icon
Unless there is a real intention to distribute code on different servers, keep all the code that implements a single conceptual object in t...
This highlight has been truncated due to consecutive passage length restrictions.
24%
Flag icon
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.
24%
Flag icon
Resist the temptation to add anything to the domain objects that does not closely relate to the concepts they represent. These design elements have their job to do: they express the model.
26%
Flag icon
Cluster the ENTITIES and VALUE OBJECTS into AGGREGATES and define boundaries around each. Choose one ENTITY to be the root of each AGGREGATE, and control all access to the objects inside the boundary through the root. Allow external objects to hold references to the root only. Transient references to internal members can be passed out for use within a single operation only.
27%
Flag icon
Even calling constructors couples the client to the concrete classes of the objects it is building. No change to the implementation of the domain objects can be made without changing the client, making refactoring harder.
27%
Flag icon
Even worse, if the client is part of the application layer, then responsibilities have leaked out of the domain layer altogether.
27%
Flag icon
Creation of an object can be a major operation in itself, but complex assembly operations do not fit the responsibility of the created objects. Combining such responsibilities can produce ungainly designs that are hard to understand. Making the client direct construction muddies the design of the client, breaches encapsulation of the assembled object or AGGREGATE, and overly couples the client to the implementation of the created object.
28%
Flag icon
Complex object creation is a responsibility of the domain layer, yet that task does not belong to the objects that express the model.
28%
Flag icon
Shift the responsibility for creating instances of complex objects and AGGREGATES to a separate object, which may itself have no responsibility in the domain model but is still part of the domain design. Provide an interface that encapsulates all complex assembly and that does not require the client to reference the concrete classes of the objects being instantiated. Create entire AGGREGATES as a piece, enforcing their invariants.
28%
Flag icon
The two basic requirements for any good FACTORY are 1. Each creation method is atomic and enforces all invariants of the created object or AGGREGATE. A FACTORY should only be able to produce an object in a consistent state.
28%
Flag icon
2. The FACTORY should be abstracted to the type desired, rather than the concrete class(es) created. The sophisticated FACTORY patterns in Gamma et al. 1995 help with this.
28%
Flag icon
A FACTORY is very tightly coupled to its product, so a FACTORY should be attached only to an object that has a close natural relationship with the product.
28%
Flag icon
The introduction of FACTORIES has great advantages, and is generally underused. Yet there are times when the directness of a constructor makes it the best choice. FACTORIES can actually obscure simple objects that don’t use polymorphism.
28%
Flag icon
Avoid calling constructors within constructors of other classes. Constructors should be dead simple. Complex assemblies, especially of AGGREGATES, call for FACTORIES. The threshold for choosing to use a little FACTORY METHOD isn’t high.
28%
Flag icon
• Each operation must be atomic. You have to pass in everything needed to create a complete product in a single interaction with the FACTORY. You also have to decide what will happen if creation fails, in the event that some invariant isn’t satisfied.
29%
Flag icon
The FACTORY will be coupled to its arguments. If you are not careful in your selection of input parameters, you can create a rat’s nest of dependencies.
29%
Flag icon
Use the abstract type of the arguments, not their concrete classes. The FACTORY is coupled to the concrete class of the products; it does not need to be coupled to concrete parameters also.
29%
Flag icon
When the program is assigning an identifier, the FACTORY is a good place to control it. Although the actual generation of a unique tracking ID is typically done by a database “sequence” or other infrastructure mechanism, the FACTORY knows what to ask for and where to put it.
29%
Flag icon
1. An ENTITY FACTORY used for reconstitution does not assign a new tracking ID. To do so would lose the continuity with the object’s previous incarnation. So identifying attributes must be part of the input parameters in a FACTORY reconstituting a stored object.
« Prev 1 3 4