Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy
Rate it:
Open Preview
3%
Flag icon
learning a new JavaScript framework every week is not the hardest aspect of our job. Making sense of new business domains can be far more challenging.
3%
Flag icon
Domain-driven design (DDD) proposes to attack the root cause for failed software projects from a different angle. Effective communication is the central theme of the domain-driven design tools and practices you are about to learn in this book.
3%
Flag icon
There is no sense in talking about the solution before we agree on the problem, and no sense talking about the implementation steps before we agree on the solution. Efrat Goldratt-Ashlag1
3%
Flag icon
To design and build an effective solution, you have to understand the problem. The problem, in our context, is the software system we have to build. To understand the problem, you have to understand the context within which it exists—the organization’s business strategy, and what value it seeks to gain by building the software.
5%
Flag icon
Only core subdomains provide a competitive advantage to a company. Core subdomains are the company’s strategy for differentiating itself from its competitors. Generic subdomains, by definition, cannot be a source for any competitive advantage. These are generic solutions—the same solutions used by the company and its competitors. Supporting subdomains have low entry barriers and cannot provide a competitive advantage either. Usually, a company wouldn’t mind its competitors copying its supporting subdomains—this won’t affect its competitiveness in the industry.
7%
Flag icon
Domain experts are subject matter experts who know all the intricacies of the business that we are going to model and implement in code.
8%
Flag icon
Core subdomains The interesting problems. These are the activities the company is performing differently from its competitors and from which it gains its competitive advantage.
8%
Flag icon
Generic subdomains The solved problems. These are the things all companies are doing in the same way. There is no room or need for innovation here; rather than creating in-house implementations, it’s more cost-effective to use existing solutions.
8%
Flag icon
Supporting subdomains The problems with obvious solutions. These are the activities the company likely has to implement in-house, but that do...
This highlight has been truncated due to consecutive passage length restrictions.
8%
Flag icon
software development is a learning process; working code is a side effect. A software project’s success depends on the effectiveness of knowledge sharing between domain experts and software engineers. We have to understand the problem in order to solve it.
8%
Flag icon
In any translation, information is lost; in this case, domain knowledge that is essential for solving business problems gets lost on its way to the software engineers.
9%
Flag icon
Domain-driven design proposes a better way to get the knowledge from domain experts to software engineers: by using a ubiquitous language.
9%
Flag icon
It’s crucial to emphasize that the ubiquitous language is the language of the business. As such, it should consist of business domain–related terms only. No technical jargon!
9%
Flag icon
A model is not a copy of the real world but a human construct that helps us make sense of real-world systems. A canonical example of a model is a map.
10%
Flag icon
To ensure effective communication, the ubiquitous language has to eliminate ambiguities and implicit assumptions. All of a language’s terms have to be consistent—no ambiguous terms and no synonymous terms.
11%
Flag icon
divide the ubiquitous language into multiple smaller languages, then assign each one to the explicit context in which it can be applied: its bounded context. In the preceding example, we can identify two bounded contexts: marketing and sales. The term lead exists in both bounded contexts, as shown in Figure 3-3. As long as it bears a single meaning in each bounded context, each fine-grained ubiquitous language is consistent and follows the domain experts’ mental models.
12%
Flag icon
One thing to beware of is splitting a coherent functionality into multiple bounded contexts. Such division will hinder the ability to evolve each context independently. Instead, the same business requirements and changes will simultaneously affect the bounded contexts and require simultaneous deployment of the changes.
12%
Flag icon
It’s crucial to remember that subdomains are discovered and bounded contexts are designed.1 The subdomains are defined by the business strategy. However, we can design the software solution and its bounded contexts to address the specific project’s context and constraints.
13%
Flag icon
Architectural design is system design. System design is contextual design—it is inherently about boundaries (what’s in, what’s out, what spans, what moves between), and about trade-offs. It reshapes what is outside, just as it shapes what is inside.
13%
Flag icon
It’s important to note that the relationship between teams and bounded contexts is one-directional: a bounded context should be owned by only one team. However, a single team can own multiple bounded contexts, as Figure 3-8 illustrates.
13%
Flag icon
A model should omit the extraneous information irrelevant to the task at hand.
16%
Flag icon
A context map can give insight into organizational issues. For example, what does it mean if a certain upstream team’s downstream consumers all resort to implementing an anticorruption layer, or if all implementations of the separate ways pattern are concentrated around the same team?
17%
Flag icon
The following patterns define different ways bounded contexts can be integrated: Partnership Bounded contexts are integrated in an ad hoc manner. Shared kernel Two or more bounded contexts are integrated by sharing a limited overlapping model that belongs to all participating bounded contexts. Conformist The consumer conforms to the service provider’s model. Anticorruption layer The consumer translates the service provider’s model into a model that fits the consumer’s needs. Open-host service The service provider implements a published language—a model optimized for its consumers’ needs. ...more
25%
Flag icon
From a business domain perspective, a useful rule of thumb is to use value objects for the domain’s elements that describe properties of other objects.
26%
Flag icon
Contrary to value objects, entities are not immutable and are expected to change. Another difference between entities and value objects is that value objects describe an entity’s properties.
28%
Flag icon
The hierarchy contains both entities and value objects, and all of them belong to the same aggregate if they are bound by the domain’s business logic. That’s why the pattern is named “aggregate”: it aggregates business entities and value objects that belong to the same transaction boundary.
28%
Flag icon
The consistency of the data can be a convenient guiding principle for designing an aggregate’s boundaries. Only the information that is required by the aggregate’s business logic to be strongly consistent should be a part of the aggregate.
30%
Flag icon
Sooner or later, you may encounter business logic that either doesn’t belong to any aggregate or value object, or that seems to be relevant to multiple aggregates. In such cases, domain-driven design proposes to implement the logic as a domain service.
30%
Flag icon
It is also important to point out that domain services have nothing to do with microservices, service-oriented architecture, or almost any other use of the word service in software engineering. It is just a stateless object used to host business logic.
37%
Flag icon
For the event sourcing pattern to work, all changes to an object’s state should be represented and persisted as events. These events become the system’s source of truth (hence the name of the pattern).
38%
Flag icon
In essence, the event sourcing pattern is nothing new. The financial industry uses events to represent changes in a ledger. A ledger is an append-only log that documents transactions. A current state (e.g., account balance) can always be deduced by “projecting” the ledger’s records.
42%
Flag icon
Layered architecture is one of the most common architectural patterns. It organizes the codebase into horizontal layers, with each layer addressing one of the following technical concerns: interaction with the consumers, implementing business logic, and persisting the data.
44%
Flag icon
The layers architecture is often confused with the N-Tier architecture, and vice versa. Despite the similarities between the two patterns, layers and tiers are conceptually different: a layer is a logical boundary, whereas a tier is a physical boundary. All layers in the layered architecture are bound by the same lifecycle: they are implemented, evolved, and deployed as one single unit. On the other hand, a tier is an independently deployable service, server, or system.
47%
Flag icon
The patterns we’ve discussed—layered architecture, ports & adapters architecture, and CQRS—should not be treated as systemwide organizational principles. These are not necessarily high-level architecture patterns for a whole bounded context either.
47%
Flag icon
The layered architecture decomposes the codebase based on its technological concerns. Since this pattern couples business logic with data access implementation, it’s a good fit for active record–based systems.
47%
Flag icon
The ports & adapters architecture inverts the relationships: it puts the business logic at the center and decouples it from all infrastructural dependencies. This pattern is a good fit for business logic implemented with the domain model pattern.
47%
Flag icon
The CQRS pattern represents the same data in multiple models. Although this pattern is obligatory for systems based on the event-sourced domain model, it can also be used in any systems that ne...
This highlight has been truncated due to consecutive passage length restrictions.
55%
Flag icon
“It depends” is the correct answer to almost any question in software engineering, but not really practical. In this chapter, we will explore what “it” depends on.
55%
Flag icon
A heuristic is not a hard rule that is guaranteed and mathematically proven to be correct in 100% of cases. Rather, it’s a rule of thumb: not guaranteed to be perfect, yet sufficient for one’s immediate goals.
55%
Flag icon
when designing bounded contexts, start with wider boundaries. If required, decompose the wide boundaries into smaller ones as you gain domain knowledge.
61%
Flag icon
As Heraclitus famously said, the only constant in life is change. Businesses are no exception. To stay competitive, companies constantly strive to evolve and reinvent themselves. Those changes should be treated as first-class elements of the design process. As the business domain evolves, changes to its subdomains must be identified and acted on in the system’s design. Make sure your past design decisions are aligned with the current state of the business domain and its subdomains. When needed, evolve your design to better match the current business strategy and needs.
64%
Flag icon
my experience shows that remote EventStorming sessions are more effective with a smaller number of participants. While as many as 10 people can attend an in-person EventStorming session, I prefer to limit online sessions to five participants. When you need more participants to contribute their knowledge, you can facilitate multiple sessions, and afterward compare and merge the resultant models.
64%
Flag icon
EventStorming is a collaboration-based workshop for modeling business processes. Apart from the resultant models, its primary benefit is knowledge sharing. By the end of the session, all the participants will synchronize their mental models of the business process and take the first steps toward using a ubiquitous language. EventStorming is like riding a bicycle. It’s much easier to learn by doing it than to read about it in a book. Nevertheless, the workshop is fun and easy to facilitate. You don’t need to be an EventStorming black belt to get started. Just facilitate the session, follow the ...more
64%
Flag icon
Let’s consider a scenario in which you are working on a greenfield project. All of your coworkers have a strong grasp of domain-driven design, and right from the get-go all are doing their best to design effective models and, of course, are devotedly using the ubiquitous language. As the project advances, the bounded contexts’ boundaries are explicit and effective in protecting the business domain models. Finally, since all tactical design decisions are aligned with the business strategy, the codebase is always in great shape: it speaks the ubiquitous language and implements the design ...more
66%
Flag icon
The strangler migration pattern is based on the same growth dynamic as the tree the pattern is named after. The idea is to create a new bounded context—the strangler—use it to implement new requirements, and gradually migrate the legacy context’s functionality into it. At the same time, except for hotfixes and other emergencies, the evolution and development of the legacy bounded context stops. Eventually, all functionality is migrated to the new bounded context—the strangler— and following the analogy, leading to the death of the host—the legacy codebase.
66%
Flag icon
An alternative to strangler-based migration is modernizing the legacy codebase in place, also called refactoring.
67%
Flag icon
Domain-driven design is about letting your business domain drive software design decisions.
67%
Flag icon
When discussing tactical design patterns, don’t appeal to authority: “Let’s use an aggregate here because the DDD book says so!” Instead, appeal to logic. For example: Why are explicit transactional boundaries important? To protect the consistency of the data. Why can’t a database transaction modify more than one instance of an aggregate? To ensure that the consistency boundaries are correct. Why can’t an aggregate’s state be modified directly by an external component? To ensure that all the related business logic is colocated and not duplicated. Why can’t we offload some of the aggregate’s ...more
68%
Flag icon
Following the simplistic decomposition heuristic of having each service expose only a single method proved to be suboptimal for many reasons. First, it’s simply not possible. Since the services have to work together, we were forced to expand their public interfaces with integration-related public methods. Second, we won the battle but lost the war. Each service ended up being much simpler than the original design, however the resultant system became orders of magnitude more complex.
69%
Flag icon
To design a proper microservices-based system, we have to optimize both global and local complexities. Setting the design goal of optimizing either one individually is a local optima. The global optima balances both complexities.
« Prev 1