I recently finished reading Domain Modeling Made Functional by Scott Wlaschin and experienced a quite enjoyable refreshment of domain driven design concepts and approaches. I also made first contact with the F# language and learned a lot about the type system and the composability of functions. What I especially enjoyed was that the first couple of chapters had a strong focus on a lot of topics around strategic design and gave really great examples for how to work out and represent business workflows. While the book talked about functional architectures I also was reminded of the types of communication and kinds of contracts between bounded contexts and how important it is to make them explicit, especially when working across teams and departments.
I found myself nodding along with many of the explanations around the domain driven design ideas, as I was familiar with a lot of the theory and have read a couple of books on it. The book does not asume any pre existing knowledge and guides the reader through the process of domain discovery and modeling using tools like event storming, interviews with domain experts, context maps and documenting command workflows in form of diagrams and written text. A typical order taking context as seen in many of the other books and examples around this topic was used to provide some backstory.
Who Will Benefit from This Book and How?
This book will be interesting for any developer or architect and will be less relevant but still interesting for product owners working with teams that strive to do domain-driven design (at least the first couple of chapters on domain discovery). The contents provide an example of how the discovery and design phase of a domain can look like and how to translate the language and workflows of the domain into functional pipelines and the F# type system.
Notes
These are the notes I captured from the book for future reference.
Context Maps
A context map shows the big picture without going into the details of a design. It shows the various bounded contexts and their relationships at a high level. The goal is to provide a view of the system as a whole. These contexts need to be discovered first and the book does a good job of telling a story of how to get there.
What I like is the information conveyed in such a simple map that can also be drawn rather quickly. In this map example above (left) we can informally say that the shipping context is downstream and the order-taking context is upstream. The upstream context typically has more influence over the format of the events, but sometimes the downstream context is inflexible (when working with a legacy system) and either the upstream context must adapt to that, or some sort of translator component will be needed as an intermediary. Thinking in terms of upstream/downstream components also helps to see where certain business processes originated from and where they continue.
The map example above (right) includes the relationships between the contexts in the diagram. These relationships are no longer purely technical but also show the relationships between the teams that own the contexts and how we expect them to collaborate (or not). The book reminded me that how domains interact is often just as much an organizational challenge as it is a technical one. The company I work for is currently in the process of restructuring the platform towards team topologies and I think context maps that explicitly define relationships will come in handy while doing so.
Documenting the domain
For the past couple of years I have been working on a CQRS style application in a microservice context within a team that tries to do its best at domain-driven design. The book lays out a domain discovery process which results in a timeline of the events and processes within the domain (event storming) and further maps them to commands that represent certain parts of a business workflow. I liked the visual and text based representations of those commands and the fact that they nicely match what the team that I work in has been doing in the codebase already. We never really drew them out that way but I like the idea of doing so when onboarding new collegues or during team meetings when talking about existing or new workflows.
In the diagram above a workflow is represented given its inputs, outputs and desired side-effects. Business processes often rely on other parts in the domain that provide/validate data which is also made visible in the diagram. The input to a workflow is always the data associated with a command and the ouptut is always a set of events to communicate to other contexts.
Below is an example from the book for the place order workflow. The same workflow is shown in a diagram and in written text. I have not seen a text based representation like this before and I liked how it captures the domain in a slightly structured way. The example was extended throughout the book to also include the substeps of the workflow in pseudocode which can be useful when talking it through in a team.
Bounded context: Order-Taking
Workflow: "Place order"
triggered by:
"Order form received" event (when Quote is not checked)
primary input:
An order form
other input:
Product catalog
output events:
"Order Placed" event
side-effects:
An acknowledgement is sent to the customer, along with the placed order
data Order =
CustomerInfo
AND ShippingAddress
AND BillingAddress
AND list of OrderLines
AND AmountToBill
data OrderLine =
Product
AND Quantity
AND Price
Terminology
Terminology for business activities:
-
A scenario describes a goal that a customer (or other user) wants to achieve, such as placing an order. It is similar to a "story" in agile development. A use case is a more detailed version of a scenario, which describes in general terms the user interactions and other steps the user needs to take to accomplish a goal. Both have a user-centric focus.
-
A business process describes a goal that the business (rather than an individual user) wants to achieve. It's similar to a scenario but has a business-centric focus rather than a user-centric focus.
-
A workflow is a detailed description of part of a business process. It lists the exact steps that an employee (or a software component) needs to do to accomplish a business goal or subgoal. Limit a workflow to what a single person or team can do, so that when a business process is spread over multiple teams, we can divide the overall business process into a series of smaller workflows, which are then coordinated in some way.
Concepts of Domain-Driven-Design:
-
A domain is an area of knowledge associated with the problem we are trying to solve, or simply, that which a "domain expert" is expert in.
-
A Domain Model is a set of simplifications that represent those aspects of a domain that are relevant to a particular problem. The domain model is part of the solution space, while the domain that it represents is part of the problem space.
-
The Ubiquitous Language is a set of concepts and vocabulary that is associated with the domain and is shared by both the team members and the source code.
-
A bounded context is a subsystem in the solution space with clear boundaries that distinguish it from other subsystems. A bounded context often corresponds to a subdomain in the problem space. Has its own set of concepts and vocabulary, its own dialect of the Ubiquitous Language.
-
A Context Map is a high-level diagram showing a collection of bounded contexts and the relationships between them.
-
A Domain Event is a record of something that happened in the system. It is always described in the past tense. An event often triggers additional activity.
-
A Command is a request for some process to happen and is triggered by a person or another event. If the process succeeds, the state of the system changes and one ore more Domain Events are recorded.
Four levels of the C4 approach:
-
The system context is the top level, representing the entire system
-
The system context comprises a number of containers, which are deployable units such as a website, a web service, a database, and so on.
-
Each container in turn comprises a number of components which are the major structural building blocks in the code.
-
Finally, each component comprises a number of classes that contain a set of low-level methods or functions.
Relationships between contexts:
-
A Shared Kernel relationship is where two contexts share some common domain design, so the teams involved must collaborate. For example a order-taking and shipping context must use the same design for a delivery address.
-
A Customer/Supplier or Consumer Driven Contract relationship is where the downstream context defines the contract that they want the upstream context to provide. The two domains can still evolve independently, as long as the upstream context fulfills its obligations under the contract. Upstream sends only the data downstream requires and no more.
-
A Conformist relationship is the opposite of consumer-driven. The downstream context accepts the contract provided by the upstream context and adapts its own domain model to match.
-
A Anti-Corruption Layer is a extra level of decoupling between contexts and acts as a translator and input gate to protect the internal domain model from knowledge of the outside world.