Methods for managing complex software construction following the practices, principles and patterns of Domain-Driven Design with code examples in C# This book presents the philosophy of Domain-Driven Design (DDD) in a down-to-earth and practical manner for experienced developers building applications for complex domains. A focus is placed on the principles and practices of decomposing a complex problem space as well as the implementation patterns and best practices for shaping a maintainable solution space. You will learn how to build effective domain models through the use of tactical patterns and how to retain their integrity by applying the strategic patterns of DDD. Full end-to-end coding examples demonstrate techniques for integrating a decomposed and distributed solution space while coding best practices and patterns advise you on how to architect applications for maintenance and scale. * Offers a thorough introduction to the philosophy of DDD for professional developers * Includes masses of code and examples of concept in action that other books have only covered theoretically * Covers the patterns of CQRS, Messaging, REST, Event Sourcing and Event-Driven Architectures * Also ideal for Java developers who want to better understand the implementation of DDD
Sie lesen das E-Book in den Legimi-Apps auf:
Overview of the Book and Technology
How This Book Is Organized
Who Should Read This Book
Part I: The Principles and Practices of Domain-Driven Design
Chapter 1: What Is Domain-Driven Design?
The Challenges of Creating Software for Complex Problem Domains
How the Patterns of Domain-Driven Design Manage Complexity
The Practices and Principles of Domain-Driven Design
Popular Misconceptions of Domain-Driven Design
The Salient Points
Chapter 2: Distilling the Problem Domain
Knowledge Crunching and Collaboration
Gaining Domain Insight with Domain Experts
Patterns for Effective Knowledge Crunching
Look For Existing Models
The Salient Points
Chapter 3: Focusing on the Core Domain
Why Decompose a Problem Domain?
How to Capture the Essence of the Problem
How to Focus on the Core Problem
How Subdomains Shape a Solution
Not All Parts of a System Will Be Well Designed
What If You Have No Core Domain?
The Salient Points
Chapter 4: Model-Driven Design
What Is a Domain Model?
Using a Ubiquitous Language to Bind the Analysis to the Code Model
Collaborating on a Ubiquitous Language
How to Create Effective Domain Models
When to Apply Model-Driven Design
The Salient Points
Chapter 5: Domain Model Implementation Patterns
The Domain Layer
Domain Model Implementation Patterns
The Salient Points
Chapter 6: Maintaining the Integrity of Domain Models with Bounded Contexts
The Challenges of a Single Model
Use Bounded Contexts to Divide and Conquer a Large Model
Implementing Bounded Contexts
The Salient Points
Chapter 7: Context Mapping
A Reality Map
Recognising the Relationships between Bounded Contexts
Communicating the Context Map
The Strategic Importance of Context Maps
The Salient Points
Chapter 8: Application Architecture
The Salient Points
Chapter 9: Common Problems for Teams Starting Out with Domain-Driven Design
Overemphasizing the Importance of Tactical Patterns
Missing the Real Value of DDD: Collaboration, Communication, and Context
Spending Too Much Time on What’s Not Important
Making Simple Problems Complex
Underestimating the Cost of Applying DDD
The Salient Points
Chapter 10: Applying the Principles, Practices, and Patterns of DDD
Applying the Principles of DDD
Exploration and Experimentation
Making the Implicit Explicit
A Problem Solver First, A Technologist Second
How Do I Know That I Am Doing It Right?
The Salient Points
Part II: Strategic Patterns: Communicating Between Bounded Contexts
Chapter 11: Introduction to Bounded Context Integration
How to Integrate Bounded Contexts
Integrating Distributed Bounded Contexts
The Challenges of DDD with Distributed Systems
SOA and Reactive DDD
The Salient Points
Chapter 12: Integrating via Messaging
Building an E-Commerce Application with NServiceBus
Maintaining a Messaging Application
Integrating a Bounded Context with Mass Transit
The Salient Points
Chapter 13: Integrating via HTTP with RPC and REST
Why Prefer HTTP?
The Salient Points
Part III: Tactical Patterns: Creating Effective Domain Models
Chapter 14: Introducing the Domain Modeling Building Blocks
Patterns to Model Your Domain
The Salient Points
Chapter 15: Value Objects
When to Use a Value Object
Common Modeling Patterns
The Salient Points
Chapter 16: Entities
Common Entity Modeling Principles and Patterns
The Salient Points
Chapter 17: Domain Services
Understanding Domain Services
Utilizing Domain Services
The Salient Points
Chapter 18: Domain Events
Essence of the Domain Events Pattern
Event Handling Actions
Domain Events’ Implementation Patterns
Testing Domain Events
The Salient Points
Chapter 19: Aggregates
Managing Complex Object Graphs
Defining Aggregate Boundaries
The Salient Points
Chapter 20: Factories
The Role of a Factory
The Salient Points
Chapter 21: Repositories
A Misunderstood Pattern
Aggregate Persistence Strategies
A Repository Is an Explicit Contract
Transaction Management and Units of Work
To Save or Not To Save
The Repository as an Anticorruption Layer
Other Responsibilities of a Repository
The Salient Points
Chapter 22: Event Sourcing
The Limitations of Storing State as a Snapshot
Gaining Competitive Advantage by Storing State as a Stream of Events
Building an Event Store
Using the Purpose-Built Event Store
CQRS with Event Sourcing
Recapping the Benefits of Event Sourcing
Weighing the Costs of Event Sourcing
Additional Learning Resources
The Salient Points
Part IV: Design Patterns for Effective Applications
Chapter 23: Architecting Application User Interfaces
Example 1: An HTML API-Based, Server-Side UI for Nondistributed Bounded Contexts
Example 2: A Data API-Based, Client-Side UI for Distributed Bounded Contexts
The Salient Points
Chapter 24: CQRS: An Architecture of a Bounded Context
The Challenges of Maintaining a Single Model for Two Contexts
A Better Architecture for Complex Bounded Contexts
The Command Side: Business Tasks
The Query Side: Domain Reporting
The Misconceptions of CQRS
Patterns to Enable Your Application to Scale
The Salient Points
Chapter 25: Commands: Application Service Patterns for Processing Business Use Cases
Differentiating Application Logic and Domain Logic
Application Service Patterns
Testing Application Services
The Salient Points
Chapter 26: Queries: Domain Reporting
Domain Reporting Within a Bounded Context
Domain Reporting Across Bounded Contexts
The Salient Points
About the Author
Figure I.1 A blueprint of the problem space of DDD.
Figure I.2 A blueprint of the solution space of Domain-Driven Design.
Figure 1.1 Complexity in software.
Figure 1.2 Code rot.
Figure 1.3 Applying the strategic patterns of Domain-Driven Design.
Figure 1.4 DDD patterns that are applicable to the problem space.
Figure 1.5 DDD patterns that are applicable to the solution space.
Figure 2.1 Knowledge crunching.
Figure 2.2 The roles of stakeholders, domain experts, and the development team.
Figure 2.3 An impact map.
Figure 2.4 A Business Model Canvas.
Figure 3.1 Cuts of a pig.
Figure 3.2 The domain of an online auction site.
Figure 3.3 The domain of an online auction site distilled into subdomains.
Figure 3.4 The distilled domain of an online auction site partitioned into core, generic, and supporting domains.
Figure 3.5 How a solution maps to the subdomains of the auction system.
Figure 3.6 Dealing with legacy.
Figure 4.1 The role of a domain model.
Figure 4.2 The domain versus the domain model.
Figure 4.3 The binding between the code and analysis model.
Figure 4.4 The problems with upfront design.
Figure 4.5 The code model and the analysis model are kept in synergy.
Figure 4.6 Team modeling.
Figure 4.7 Translation costs of the project.
Figure 4.8 London Tube map bearing little resemblance to the distance between stations.
Figure 5.1 The code that represents the domain model makes up only a small portion of the overall codebase.
Figure 5.2 Multiple domain models implemented in various patterns inside an application.
Figure 5.3 The domain model pattern.
Figure 5.4 The domain model of an auction site.
Figure 5.5 The transaction script pattern.
Figure 5.6 The transaction script pattern UML.
Figure 6.1 A model will grow in complexity.
Figure 6.2 Complexity in a model increases with multiple teams.
Figure 6.3 Domain terms mean different things in different contexts.
Figure 6.4 The same concept should be understood within different contexts.
Figure 6.5 A single view of an entity in the domain for all subdomains can quickly become a problem.
Figure 6.6 The product, an implementation of the god object antipattern.
Figure 6.7 The difference between an enterprise model and a domain model.
Figure 6.8 Putting terms into context and identifying multiple models.
Figure 6.9 Define each model within its own context.
Figure 6.10 A layered architecture pattern per bounded context and not per application.
Figure 6.11 The Product class in different contexts.
Figure 6.12 You can apply different architectural patterns to the different bounded contexts.
Figure 6.13 The anatomy of a bounded context.
Figure 7.1 A context map.
Figure 7.2 The technical integration on a context map.
Figure 7.3 The organizational relationships on a context map.
Figure 7.4 Use an anticorruption layer to integrate with code you don’t own or can’t change.
Figure 7.5 Integration with a shared kernel.
Figure 7.6 Multiple subsystems integrating with similar transformation efforts.
Figure 7.7 Integration with an open host service.
Figure 7.8 A customer-supplier relationship between bounded contexts.
Figure 7.9 A context map showing the types of integration between bounded contexts.
Figure 8.1 A layered architecture.
Figure 8.2 Dependency inversion within a layered architecture.
Figure 8.3 Domain objects are hidden from clients of the application.
Figure 8.4 Testing layers in isolation.
Figure 8.5 Bounded contexts integrating via a shared data schema.
Figure 8.6 Bounded contexts with their own data schema.
Figure 8.7 Bounded contexts integrating via a separate application layer.
Figure 8.8 Presentation layer composed of bounded contexts.
Figure 8.9 Application logic versus domain logic.
Figure 8.10 A view model mapping to many domain objects.
Figure 8.11 View models queried directly from the data source.
Figure 8.12 View store separated from transactional storage.
Figure 8.13 :Various clients of an application.
Figure 8.14 A system composed of multiple bounded contexts.
Figure 8.15 A process manager.
Figure 10.1 The process of DDD.
Figure 11.1 Multiple bounded contexts inside a single solution.
Figure 11.2 Multiple bounded contexts using a shared schema.
Figure 11.3 Autonomous bounded contexts with a shared-nothing architecture.
Figure 11.4 A bubble context.
Figure 11.5 An autonomous bubble context.
Figure 11.6 Exposing a legacy context as a JSON web service.
Figure 11.7 Database integration.
Figure 11.8 Flat file integration.
Figure 11.9 E-commerce system using synchronous RPC.
Figure 11.10 Replacing RPC with reactive.
Figure 11.11 Decomposing the shipping bounded context into business components.
Figure 11.12 Shipping bounded context broken down into business components and components.
Figure 11.13 Possible deployment view of components in the Shipping bounded context.
Figure 11.14 Sharing dependencies is only allowed between components inside the same business component.
Figure 12.1 Message bus architecture.
Figure 12.2 Adding new event subscribers doesn’t affect existing code.
Figure 12.3 A component diagram showing domain events.
Figure 12.4 A containers diagram for a typical e-commerce application.
Figure 12.5 The basic web page for placing an order.
Figure 12.6 Creating an empty Visual Studio solution.
Figure 12.7 Adding the DDDesign.Web MVC 4 web application.
Figure 12.8 Selecting empty ASP.NET MVC template with the Razor view engine.
Figure 12.9 Adding a reference to the Sales.Messages project from the DDDesign.Web project.
Figure 12.10 Installing NServiceBus with the NuGet package manager console.
Figure 12.11 Adding the Orders controller.
Figure 12.12 Solution structure after adding Orders controller and view.
Figure 12.13 Selecting solution properties.
Figure 12.14 Configuring each project to start up.
Figure 12.15 An NServiceBus server setting up an application.
Figure 12.16 Adding the OrderCreated event to the Sales.Messages project.
Figure 12.17 Adding the two messages to the Billing.Messages project.
Figure 12.18 Setting all your NServiceBus servers to start up.
Figure 12.19 Looks like the payment was processed successfully.
Figure 12.20 Actually, the payment failed twice first.
Figure 12.21 The payment rejected use case.
Figure 12.22 Multiple bounded contexts store the same piece of data locally.
Figure 12.23 Shipping bounded context storing data locally and using it to arrange shipping.
Figure 12.24 Business components have their own APIs.
Figure 12.25 Pages get their data from multiple APIs.
Figure 12.26 Locating the error queue in Visual Studio’s Server Explorer.
Figure 12.27 Viewing the contents of a message in the error queue.
Figure 12.28 Promotions bounded context integrating into e-commerce messaging system.
Figure 12.29 A messaging bridge is like a link.
Figure 12.30 Mass Transit receiving messages via the bridge.
Figure 13.1 The “find recommended users” use case.
Figure 13.2 Adding the Account Management WCF Service.
Figure 13.3 Visual Studio’s WCF test client.
Figure 13.4 Invoking an RPC in WCF’s test client.
Figure 13.5 Adding a Service Reference in Visual Studio.
Figure 13.6 Generated proxy classes.
Figure 13.7 The RPC must have occurred.
Figure 13.8 Viewing the output of a Web API controller in a browser.
Figure 13.9 Component diagram for the Recommended Accounts use case.
Figure 13.10 Containers diagram of Discovery, Account Management, and Marketing bounded contexts.
Figure 13.11 Flow of HTTP requests for the Add Follower use case.
Figure 13.12 Flow of HTTP requests for polling and consuming the Began Following event feed.
Figure 13.13 Accessing the HAL browser.
Figure 13.14 Viewing the entry point resource in the HAL browser.
Figure 13.15 Following the Accounts link in the HAL browser.
Figure 13.16 Resource’s data fields are represented as plain JSON.
Figure 13.17 The Event Store’s admin UI.
Figure 13.18 The NON-GET button in the HAL browser.
Figure 13.19 Constructing JSON on the NON-GET dialog in the HAL browser.
Figure 13.20 Viewing an event in the Event Store.
Figure 13.21 Feed consumer processing events
Figure 14.1 Tactical patterns—domain model building blocks.
Figure 14.2 An entity.
Figure 14.3 A value object.
Figure 14.4 Modules used to organize domain concepts within a domain model.
Figure 14.5 A large object graph.
Figure 14.6 A large object graph split into aggregates.
Figure 14.7 An aggregate root acts as the entry point to the aggregate.
Figure 14.8 Aggregates should be based around invariants.
Figure 14.9 An aggregate root acts as the entry point to the aggregate.
Figure 14.10 A factory.
Figure 14.11 A repository.
Figure 14.12 A domain event.
Figure 14.13 Storing events, not snapshots
Figure 15.1 Value object stored in custom format.
Figure 15.2 Separate table for Customer and Name.
Figure 15.3 Customer foreign key used to join Customer and Name.
Figure 16.1 Creating a client-side GUID and posting to multiple back-end services.
Figure 18.1 Ensuring correct transactional behavior
Figure 18.2 Flow of internal and external events in a typical business use case
Figure 19.1 Complexity-causing bidirectional relationships.
Figure 19.2 Constraining a bidirectional association.
Figure 19.3 Qualifying associations.
Figure 19.4 Qualifying associations with filter criteria from the Ubiquitous Language (UL).
Figure 19.5 Modeling relationships with object references increases complexity.
Figure 19.6 Simplified relationships using IDs instead of object references.
Figure 19.7 Complex domain model with an abundance of unnecessary associations.
Figure 19.8 Clearer domain model based only on essential associations.
Figure 19.9 Locking caused by large transactional boundary.
Figure 19.10 Inconsistent data arising from lack of transactional boundaries.
Figure 19.11 Aligning transactional boundaries with domain invariants.
Figure 19.12 Enforcing consistency with help from aggregate roots.
Figure 19.13 Aggregates are eventually consistent externally.
Figure 19.14 Eventually consistent Loyalty aggregate.
Figure 19.15 eBidder bounded contexts.
Figure 19.16 Listing bounded context aggregates.
Figure 19.17 Question aggregate boundary definition.
Figure 19.18 Auction aggregate boundary definition.
Figure 19.19 Listing aggregate boundary definition.
Figure 19.20 All aggregate boundary definitions.
Figure 19.21 Aligning aggregates with transactional boundaries.
Figure 19.22 Aggregate roots are the gateway into an aggregate.
Figure 19.23 Aggregate roots have global identity.
Figure 19.24 Consumers of an aggregate may not hold a reference to the aggregate’s internal members.
Figure 19.25 Sharing information between aggregates by using copies of internal objects.
Figure 19.26 Non aggregate roots can hold a reference to roots from other aggregates.
Figure 19.27 Aggregates should be loaded from a database entirely to protect their integrity.
Figure 19.28 Loading aggregates versus going directly to the database.
Figure 19.29 Aggregate boundaries are consistency boundaries.
Figure 19.30 Eventual consistency using database integration.
Figure 21.1 An ORM maps between the domain and persistence model.
Figure 21.2 An ORM maps between the domain and the persistence model.
Figure 21.3 An aggregate can be serialized and stored.
Figure 21.4 The memento pattern enables you to map a snapshot of the domain model to the persistence model.
Figure 21.5 Save the events that have occurred to an aggregate.
Figure 21.6 The unit-of-work pattern.
Figure 21.7 The repository acts as an anticorruption layer.
Figure 21.8 Solution object model.
Figure 21.9 Visual Solution project structure
Figure 21.10 NuGet Package Manager
Figure 21.11 The Visual Studio solution structure
Figure 21.12 The build action property of the XML Mapping file
Figure 21.13 The database schema
Figure 21.14 The running program
Figure 21.15 Install RavenDB client libraries via NuGet.
Figure 21.16 Solution explorer with an application based on the NHibernate sample.
Figure 21.17 Inspecting the document inside RavenDB Management Studio.
Figure 21.18 Install Entity Framework client libraries via NuGet.
Figure 21.19 Solution explorer with an application based on the NHibernate sample.
Figure 21.20 Install Dapper client libraries via NuGet.
Figure 21.21 Skeleton solution based on the Entity Framework solution.
Figure 21.22 Add a reference to System.Configuration.
Figure 21.23 Add a reference to System.Transactions.
Figure 22.1 Calculating the state for any point in history by replaying events.
Figure 22.2 Creating projections from multiple event streams.
Figure 22.3 Efficiently restoring state by using snapshots.
Figure 22.4 Event Store’s stream tab indicating the test data was successfully inserted.
Figure 22.5 Result of running a query in the Event Store web UI.
Figure 22.6 Creating a projection in the web UI.
Figure 22.7 One event stream used to support many different queries and use cases.
Figure 22.8 Creating materialized/denormalized views of the event stream to support each use case.
Figure 23.1 UI for autonomous applications.
Figure 23.2 UI that defers to authority.
Figure 23.3 Composing a web page with HTML provided by bounded contexts.
Figure 23.4 Pulling in data from multiple bounded contexts.
Figure 23.5 Aggregating on the client.
Figure 23.6 Aggregating on the server.
Figure 23.7 The design for this example.
Figure 23.8 Rendering of composite UI.
Figure 23.9 The design for this example.
Figure 23.10 Client-side JSON API composition in action.
Figure 24.1 A single model fulfilling the read and write sides of an application.
Figure 24.2 The CQRS pattern with a separate read and write model.
Figure 24.3 The command side of CQRS.
Figure 24.4 A user interface pulling data from many aggregates.
Figure 24.5 The query side of CQRS.
Figure 24.6 Using a different data store for querying.
Figure 24.7 An eventually-consistent read model.
Figure 24.8 Consolidate data from many bounded contexts into a single read model.
Figure 24.9 Use a copy of the transactional database for the read model.
Figure 24.10 An asynchronous write side.
Figure 24.11 Scaling out the read and write sides of CQRS.
Figure 25.1 Where application services fit in.
Figure 25.2 The Recommend-a-Friend use case.
Figure 25.3 Transactional boundary for Recommend-a-Friend.
Figure 26.1 Denormalized view cache for the loyalty report.
Figure 26.2 Projecting the “diagnoses” event stream onto event streams representing the monthly summary of each diagnosis.
Figure 26.3 Aggregating data from multiple bounded contexts into a single report.
Figure 26.4 Standard reporting context.
Figure 26.5 Complex data-processing reporting context.
Table of Contents
WRITING SOFTWARE IS EASY— at least if it’s greenfield software. When it comes to modifying code written by other developers or code you wrote six months ago, it can be a bit of a bore at best and a nightmare at worst. The software works, but you aren’t sure exactly how. It contains all the right frameworks and patterns, and has been created using an agile approach, but introducing new features into the codebase is harder than it should be. Even business experts aren’t helpful because the code bears no resemblance to the language they use. Working on such systems becomes a chore, leaving developers frustrated and devoid of any coding pleasure.
Domain-Driven Design (DDD) is a process that aligns your code with the reality of your problem domain. As your product evolves, adding new features becomes as easy as it was in the good old days of greenfield development. Although DDD understands the need for software patterns, principles, methodologies, and frameworks, it values developers and domain experts working together to understand domain concepts, policies, and logic equally. With a greater knowledge of the problem domain and a synergy with the business, developers are more likely to build software that is more readable and easier to adapt for future enhancement.
Following the DDD philosophy will give developers the knowledge and skills they need to tackle large or complex business systems effectively. Future enhancement requests won’t be met with an air of dread, and developers will no longer have stigma attached to the legacy application. In fact, the term legacy will be recategorized in a developer’s mind as meaning this: a system that continues to give value for the business.
This book provides a thorough understanding of how you can apply the patterns and practices of DDD on your own projects, but before delving into the details, it’s good to take a bird’s-eye view of the philosophy so you can get a sense of what DDD is really all about.
Before you can develop a solution, you must understand the problem. DDD emphasizes the need to focus on the business problem domain: its terminology, the core reasons behind why the software is being developed, and what success means to the business. The need for the development team to value domain knowledge just as much as technical expertise is vital to gain a deeper insight into the problem domain and to decompose large domains into smaller subdomains.
Figure I.1 shows a high-level overview of the problem space of DDD that will be introduced in the first part of this book.
FIGURE I.1 A blueprint of the problem space of DDD.
When you have a sound understanding of the problem domain, strategic patterns of DDD can help you implement a technical solution in synergy with the problem space. Patterns enable core parts of your system that are crucial to the success of the product to be protected from the generic areas. Isolating integral components allows them to be modified without having a rippling effect throughout the system.
Core parts of your product that are sufficiently complex or will frequently change should be based on a model. The tactical patterns of DDD along with Model-Driven Design will help you create a useful model of your domain in code. A model is the home to all of the domain logic that enables your application to fulfill business use cases. A model is kept separate from technical complexities to enable business rules and policies to evolve. A model that is in synergy with the problem domain will enable your software to be adaptable and understood by other developers and business experts.
Figure I.2 shows a high-level overview of the solution space of DDD that is introduced in the first part of this book.
FIGURE I.2 A blueprint of the solution space of Domain-Driven Design.
This book is divided into four parts. Part I focuses on the philosophy, principles, and practices of DDD. Part II details the strategic patterns of integrating bounded contexts. Part III covers tactical patterns for creating effective domain models. Part IV delves into design patterns you can apply to utilize the domain model and build effective applications.
Part I introduces you to the principles and practices of DDD.
DDD is a philosophy to help with the challenges of building software for complex domains. This chapter introduces the philosophy and explains why language, collaboration, and context are the most important facets of DDD and why it is much more than a collection of coding patterns.
Making sense of a complex problem domain is essential to creating maintainable software. Knowledge crunching with domain experts is key to unlocking that knowledge. Chapter 2 details techniques to enable development teams to collaborate, experiment, and learn with domain experts to create an effective domain model.
Chapter 3 explains how to distill large problem domains and identify the most important part of a problem: the core domain. It then explains why you should focus time and energy in the core domain and isolate it from the less important supporting and generic domains.
Business colleagues understand an analysis model based on the problem area you are working within. Development teams have their own code version of this model. In order for business and technical teams to collaborate a single model is needed. A ubiquitous language and a shared understanding of the problem space is what binds the analysis model to the code model. The idea of a shared language is core to DDD and underpins the philosophy. A language describing the terms and concepts of the domain, which is created by both the development team and the business experts, is vital to aid communication on complex systems.
Chapter 5 expands on the role of the domain model within your application and the responsibilities it takes on. The chapter also presents the various patterns that can be used to implement a domain model and what situations they are most appropriate for.
In large solutions more than a single model may exist. It is important to protect the integrity of each model to remove the chance of ambiguity in the language and concepts being reused inappropriately by different teams. The strategic pattern known as bounded context is designed to isolate and protect a model in a context while ensuring it can collaborate with other models.
Using a context map to understand the relationships between different models in an application and how they integrate is vital for strategic design. It is not only the technical integrations that context maps cover but also the political relationships between teams. Context maps provide a view of the landscape that can help teams understand their model in the context of the entire landscape.
An application needs to be able to utilize the domain model to satisfy business use cases. Chapter 8 introduces architectural patterns to structure your applications to retain the integrity of your domain model.
Chapter 9 describes the common issues teams face when applying DDD and why it’s important to know when not to use it. The chapter also focuses on why applying DDD to simple problems can lead to overdesigned systems and needless complexity.
Chapter 10 covers techniques to sell DDD and to start applying the principles and practices to your projects. It explains how exploration and experimentation are more useful to build great software than trying to create the perfect domain model.
Part II shows you how to integrate bounded contexts, and offers details on the options open for architecting bounded contexts. Code examples are presented that detail how to integrate with legacy applications. Also included are techniques for communicating across bounded contexts.
Modern software applications are distributed systems that have scalability and reliability requirements. This chapter blends distributed systems theory with DDD so that you can have the best of both worlds.
A sample application is built showing how to apply distributed systems principles synergistically with DDD using a message bus for asynchronous messaging.
Another sample application is built showing an alternative approach to building asynchronous distributed systems. This approach uses standard protocols like Hypertext Transport Protocol (HTTP), REST, and Atom instead of a message bus.
Part III covers the design patterns you can use to build a domain model in code, along with patterns to persist your model and patterns to manage the lifecycles of the domain objects that form your model.
This chapter is an introduction to all the tactical patterns at your disposal that allow you to build an effective domain model. The chapter highlights some best practice guidelines that produce more manageable and expressive models in code.
This is an introduction to the DDD modeling construct that represents identityless domain concepts like money.
Entities are domain concepts that have an identity, such as customers, transactions, and hotels. This chapter covers a variety of examples and complementary implementation patterns.
Some domain concepts are stateless operations that do not belong to a value object or an entity. They are known as domain services.
In many domains, focusing on events reveals greater insight than focusing on just entities. This chapter introduces the domain event design pattern that allows you to express events more clearly in your domain model.
Aggregates are clusters of domain objects that represent domain concepts. Aggregates are a consistency boundary defined around invariants. They are the most powerful of the tactical patterns.
Factories are a lifecycle pattern that separate use from construction for complex domain objects.
Repositories mediate between the domain model and the underlying data model. They ensure that the domain model is kept separate from any infrastructure concerns.
Like domain events in Chapter 18, event sourcing is a useful technique for emphasizing, in code, events that occur in the problem domain. Event sourcing goes beyond domain events by storing the state of the domain model as events. This chapter provides a number of examples, including ones that use a purpose-built event store.
Part IV showcases the design patterns for architecting applications that utilize and protect the integrity of your domain model.
For systems composed of many bounded contexts, the user interface often requires the composition of data from a number of them, especially when your bounded contexts form a distributed system.
CQRS is a design pattern that creates two models where there once was one. Instead of a single model to handle the two different contexts of reads and writes, two explicit models are created to handle commands or serve queries for reports.
Learn the difference between application and domain logic to keep your model focused and your system maintainable.
Business people need information to make informed business and product-development decisions. A range of techniques for building reports that empower the business is demonstrated in this chapter.
This book introduces the main themes behind DDD—its practices, patterns, and principles along with personal experiences and interpretation of the philosophy. It is intended to be used as a learning aid for those interested in or starting out with the philosophy. It is not a replacement for Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans (Addison-Wesley Professional, 2003). Instead, it takes the concepts introduced by Evans and distills them into simple straightforward prose, with practical examples so that any developer can get up to speed with the philosophy before going on to study the subject in more depth.
This book is based on the author’s personal experiences with the subject matter. You may not always agree with it if you are a seasoned DDD practitioner, but you should still get something out of it.
As you work through the examples in this book, you may choose either to type in all the code manually, or to use the source code files that accompany the book. All the source code used in this book is available for download at www.wrox.com. Specifically for this book, the code download is on the Download Code tab at: www.wrox.com/go/domaindrivendesign. Although code examples are presented in C# .NET. The concepts and practices can be applied to any programming language.
You can also search for the book at www.wrox.com by ISBN (the ISBN for this book is 978-1-1187-1470-6) to find the code. And a complete list of code downloads for all current Wrox books is available at www.wrox.com/dynamic/books/download.aspx.
We make every effort to ensure that there are no errors in the text or in the code. However, no one is perfect, and mistakes do occur. If you find an error in one of our books, like a spelling mistake or faulty piece of code, we would be very grateful for your feedback. By sending in errata, you may save another reader hours of frustration, and at the same time, you will be helping us provide even higher quality information.
To find the errata page for this book, go to www.wrox.com/go/domaindrivendesign.
And click the Errata link. On this page you can view all errata that has been submitted for this book and posted by Wrox editors.
If you don’t spot “your” error on the Book Errata page, go to www.wrox.com/contact/techsupport.shtml and complete the form there to send us the error you have found. We’ll check the information and, if appropriate, post a message to the book’s errata page and fix the problem in subsequent editions of the book.
For author and peer discussion, join the P2P forums at http://p2p.wrox.com. The forums are a web-based system for you to post messages relating to Wrox books and related technologies and interact with other readers and technology users. The forums offer a subscription feature to e-mail you topics of interest of your choosing when new posts are made to the forums. Wrox authors, editors, other industry experts, and your fellow readers are present on these forums.
At http://p2p.wrox.com, you will find a number of different forums that will help you, not only as you read this book, but also as you develop your own applications. To join the forums, just follow these steps:
and click the Register link.
Complete the required information to join, as well as any optional information you wish to provide, and click Submit.
You will receive an e-mail with information describing how to verify your account and complete the joining process.
NOTEYou can read messages in the forums without joining P2P, but in order to post your own messages, you must join.
Once you join, you can post new messages and respond to messages other users post. You can read messages at any time on the web. If you would like to have new messages from a particular forum e-mailed to you, click the Subscribe to this Forum icon by the forum name in the forum listing.
For more information about how to use the Wrox P2P, be sure to read the P2P FAQs for answers to questions about how the forum software works, as well as many common questions specific to P2P and Wrox books. To read the FAQs, click the FAQ link on any P2P page.
The aim of this book is to present the philosophy of DDD in a down-to-earth and practical manner for experienced developers building applications for complex domains. A focus is placed on the principles and practices of decomposing a complex problem space as well as the implementation patterns and best practices for shaping a maintainable solution space. You will learn how to build effective domain models by using tactical patterns and how to retain their integrity by applying the strategic patterns of DDD.
By the end of this book, you will have a thorough understanding of DDD. You will be able to communicate its value and when to use it. You will understand that even though the tactical patterns of DDD are useful, it is the principles, practices, and strategic patterns that will help you architect applications for maintenance and scale. With the information gained within this book, you will be in a better place to manage the construction and maintenance of complex software for large and complex problem domains.
WHAT’S IN THIS CHAPTER?
An introduction to the philosophy of Domain-Driven Design
The challenges of writing software for complex problem domains
How Domain-Driven Design manages complexity
How Domain-Driven Design applies to both the problem and solution space
The strategic and tactical patterns of Domain-Driven Design
The practices and principles of Domain-Driven Design
The misconceptions of Domain-Driven Design
Domain-Driven Design (DDD) is a development philosophy defined by Eric Evans in his seminal work Domain-Driven Design: Tackling Complexity in the Heart of Software (Addison-Wesley Professional, 2003). DDD is an approach to software development that enables teams to effectively manage the construction and maintenance of software for complex problem domains.
This chapter will give you a high-level introduction to DDD’s practices, patterns, and principles along with an explanation of how it will improve your approach to software development. You will learn the value of analyzing a problem space and where to focus your efforts. You will understand why collaboration, communication, and context are so important for the design of maintainable software.
At the end of this chapter you will have a solid understanding of DDD that will provide context to the detail of the various patterns, practices, and principles that are contained throughout this book. However, before we delve into how DDD handles complexity it’s important to understand what problems can cause software to get into an unmanageable state.
To understand how DDD can help with the design of software for a nontrivial domain, you must first understand the difficulties of creating and maintaining software. By far, the most popular software architectural design pattern for business applications is the Big Ball of Mud (BBoM) pattern. The definition of BBoM, as defined by Brian Foote and Joseph Yoder in the paper “Big Ball of Mud,” is “… a haphazardly structured, sprawling, sloppy, duct-tape-and-baling-wire, spaghetti-code jungle.”
Foote and Yoder use the term BBoM to describe an application that appears to have no distinguishable architecture (think big bowl of spaghetti versus dish of layered lasagna). The issue with allowing software to dissolve into a BBoM becomes apparent when routine changes in workflow and small feature enhancements become a challenge to implement due to the difficulties in reading and understanding the existing codebase. In his book, Domain-Driven Design: Tackling Complexity in the Heart of Software (Addison-Wesley Professional, 2003), Eric Evans describes such systems as containing “code that does something useful, but without explaining how.” One of the main reasons software becomes complex and difficult to manage is due to the mixing of domain complexities with technical complexities, as illustrated in Figure 1.1.
FIGURE 1.1 Complexity in software.
A lack of focus on a shared language and knowledge of the problem domain results in a codebase that works but does not reveal the intent of the business. This makes codebases difficult to read and maintain because translations between the analysis model and the code model can be costly and error prone.
Code without a binding to an analysis model that the business understands will degrade over time and is therefore more likely to result in an architecture that resembles the BBoM pattern. Due to the cost of translation teams that do not utilize the rich vocabulary of the problem domain in code will decrease their chances of discovering new domain concepts when collaborating with business experts.
An analysis model is used to describe the logical design and structure of a software application. It can be represented as sketches or by using modeling languages such as UML. It is the representation of software that non-technical people can conceptualize in order to understand how software is constructed.
As highlighted in Figure 1.2, the initial incarnation of a system that resembles BBoM is fast to produce and often a well-rounded success, but because there is little focus based on the design of an application around a model of the problem domain, subsequent enhancements are troublesome. The codebase lacks the required synergy with the business behavior to make change manageable. Complexities of the problem domain are often mixed with the accidental complexities of the technical solution.
FIGURE 1.2 Code rot.
Continuing to persist with an architectural spaghetti-like pattern can lead to a sluggish pace of feature enhancement. When newer versions of the product are released, they can be buggy due to the unintelligible mess of the codebase that developers have to deal with. Over time, the development team increasingly complains about the difficulty of working in such a mess. Even if resources are added to the project, velocity cannot be increased to a level that satisfies the business.
In the end, exasperated by the situation, the request for the dreaded application rewrite is granted. Without due care and consideration, however, even the greenfield project can fall foul of the same issues that created the original BBoM. This entire experience can be frustrating for the business that saw a great return on investment (ROI) in terms of features and speed of delivery at the beginning but over time, even with additional investment in resources, did not see the sustained evolution of the product to meet their needs. Ultimately the BBoM is bad news for you as a developer because it’s a messy bug-prone code base that you hate dealing with. And it’s bad news for the business because it reduces their capability to rapidly deliver business value
Software projects fail when you don’t understand the business domain you are working within well enough. Typing is not the bottleneck for delivering a product; coding is the easy part of development. Outside of non-functional requirements creating and keeping a useful software model of the domain that can fulfill business-use cases is the difficult part. However, the more you invest in understanding your business domain the better equipped you will be when you are trying to model it in software to solve its inherent business problems.
A problem domain refers to the subject area for which you are building software. DDD stresses the need to focus on the domain above anything else when working on creating software for large-scale and complex business systems. Experts in the problem domain work with the development team to focus on the areas of the domain that are useful to be able to produce valuable software. For example, when writing software for the health industry to record patient treatment, it is not important to learn to become a doctor. What is important to understand is the terminology of the health industry, how different departments view patients and care, what information doctors gather, and what they do with it.
DDD deals with both the challenge of understanding a problem domain and creating a maintainable solution that is useful to solve problems within it. It achieves this by utilizing a number of strategic and tactical patterns.
The strategic patterns of DDD distil the problem domain and shape the architecture of an application.
Not all of a large software product needs be perfectly designed—in fact trying to do so would be a waste of effort. Development teams and domain experts use analysis patterns and knowledge crunching to distill large problem domains into more manageable subdomains. This distillation reveals the core sub domain—the reason the software is being written. The core domain is the driving force behind the product under development; it is the fundamental reason it is being built. DDD emphasizes the need to focus effort and talent on the core subdomain(s) as this is the area that holds the most value and is key to the success of the application.
This clarity on where to focus effort can also empower teams to look for open source off-the-shelf solutions for some of the less important parts of a system, which means that they have more time to focus on what is important and ensure that the core domain does not become a BBoM.
Discovering the core domain helps teams understand why they’re producing the software and what it means for the software to be successful to the business. It is the appreciation for the business intent that will enable the development team to identify and invest its time in the most important parts of the system. As the business evolves, so in turn must the software; it needs to be adaptable. Investment in code quality for the key areas of an application will help it change with the business. If key areas of the software are not in synergy with the business domain then, over time, it is likely that the design will rot and turn into a big ball of mud, resulting in hard-to-maintain software.
In the solution space a software model is built for each subdomain to handle domain problems and to align the software with the business contours. This model is not a model of real life but more an abstraction built to satisfy the requirements of business use cases while still retaining the rules and logic of the business domain. The development team should focus as much energy and effort on the model and domain logic as it does on the pure technical aspects of the application. To avoid accidental technical complexity the model is kept isolated from infrastructure code.
All models are not created equal; the most appropriate design patterns are used based on the complexity needs of each subdomain rather than applying a blanket design to the whole system. Models for subdomains that are not core to the success of the product or that are not as complex need not be based on rich object-oriented designs, and can instead utilize more procedural or data-driven architectures.
Models are built through the collaboration of domain experts and the development team. Communication is achieved using an ever-evolving shared language known as the ubiquitous language (UL) to efficiently and effectively connect a software model to a conceptual analysis model. The software model is bound to the analysis model by using the same terms of the UL for its structure and class design. Insights, concepts, and terms that are discovered at a coding level are replicated in the UL and therefore the analytical model. Likewise when the business reveals hidden concepts at the analysis model level this insight is fed back into the code model; this is the key that enables the domain experts and development teams to evolve the model in collaboration.
Models sit within a bounded context, which defines the applicability of the model and ensures that its integrity is retained. Larger models can be split into smaller models and defined within separate bounded contexts where ambiguity in terminology exists or where multiple teams are a working in order to further reduce complexity.
Bounded contexts are used to form a protective boundary around models that helps to prevent software from evolving into a BBoM. This is achieved by allowing the different models of the overall solution to evolve within well-defined business contexts without having a negative, rippling impact on other parts of the system. Models are isolated from infrastructure code to avoid the accidental complexity of merging technical and business concepts. Bounded contexts also prevent the integrity of models being corrupt by isolating them from third-party code.
Compare the diagram in Figure 1.3 to Figure 1.2. The diagram shows how the strategic patterns of DDD have been applied to the software to manage the large problem domain and protect discrete models within it.
FIGURE 1.3 Applying the strategic patterns of Domain-Driven Design.
Not all parts of a large application will be designed perfectly—nor do they need to be. Although it’s not advisable to build an entire enterprise software stack following the BBoM pattern, you can still utilize the pattern. Areas of low complexity or that are unlikely to be invested in can be built without the need for perfect code quality; working software is good enough. Sometimes feedback and first-to-market are core to the success of a product; in this instance, it can make business sense to get working software up as soon as possible, whatever the architecture. Code quality can always be improved after the business deems the product to be a success and worthy of prolonged investment. The key to reaping the benefits of the BBoM is to define a context around the bounded contexts that use the BBoM to avoid them corrupting the core subcomain.
„Ich bin wirklich begeistert. Auch die Möglichkeit des zusätzlichen eReaders im Abo finde ich persönlich toll.”
„Die Auswahl von Legimi ist großartig.”
„Der Leser findet seine E-Books/Hörbücher sehr schnell und sie lassen sich, ob mit oder ohne Internetverbindung problemlos öffnen.”
Wurm sucht Buch
„Ich finde das Angebot von Legimi richtig toll.”
„Besonders schön finde ich die große Auswahl an möglichen Abo-Modellen und besonders die Abos mit eReader.”
Miss Foxy Reads
„Ich muss sagen, dass ich von dem E-Reader mehr als positiv überrascht bin.”
„Das ist wirklich eine großartige Idee und mal was ganz Anderes.”
Mikka liest das Leben...
Tausende von E-Books und Hörbücher
Ihre Zahl wächst ständig und Sie haben eine Fixpreisgarantie.
Sie haben über uns geschrieben:
Dabei gewährt der E-Book-Anbieter größtmögliche Freiheiten
Größter Vorteil die Möglichkeit, in der aktuellen App komfortabel zwischen E-Book und Hörbuchversion eines Titels
Spotify for E-Books