How to apply Domain-Driven Design using Baker

Sander Dijkhuis
ING Blog
Published in
4 min readSep 21, 2020

In our first article, we introduced how Cleverbase started reinforcing its use of Domain Driven Design (DDD) with ING’s Baker library. Now, we will explain the mapping of DDD concepts to the Baker domain-specific language.

At Cleverbase, the team was learning about DDD and business process design using Baker at the same time, and we needed to discover how concepts in each domain map to others. The following part isn’t a DDD tutorial and assumes working knowledge of DDD. If you’re new to DDD or want to learn more, check:

DDD terms are marked in italic, while Baker terms are hyperlinks to the Baker documentation.

Before you start prototyping, pick a process within the domain. Whenever analyzing or designing within this domain, aim to speak and evolve the ubiquitous language associated with the domain. You could use event-storming (see EventStorming Glossary & Cheat Sheet) to discover the process through its domain events — pay attention to the lilac “policy” notes which often indicate a business process:

Illustration of event-storming, inspired by the blog article about the Aecor library and process managers by Vladimir Pavkin.

We will use Baker to design and implement a lightweight process manager to maintain the process state and determine the next step based on intermediate results. This process manager exists within a bounded context: a subsystem in the solution that corresponds to the domain, or to a subdomain if it can be broken down without losing coherence. Whenever making a design decision in this bounded context, be aware of how it affects the domain model.

Let’s look at the patterns and definitions of DDD and how they map to Baker concepts.

DDD breaks down domain activity into three categories of messages:

  • A command is a request to perform an action and is implemented as an interaction in Baker. Execution of a command modifies an underlying system of record, and results in one or more domain events or in a rejection. In Baker, either is modelled as a single interaction output, and any interaction may result in one of the specified interaction outputs.
  • A domain event is a historical fact recorded by the system. If the domain event is triggered externally, it maps 1:1 to a sensory event in Baker.
  • A query is a read-only operation, that could either be an interaction in Baker that performs a lookup of some sort (check resource availability in an inventory for example); or a call to one of Baker APIs, to retrieve the current state (list of ingredients known already) of a recipe instance or what is known to have happened (list of events) for it.

A process manager implements one or more recipes. Each recipe describes policies regarding which commands and queries should be triggered upon domain events.

A recipe for the same process.

Another building block in DDD is the domain object, which can be further broken down into the following categories:

  • A value object contained in a message is modelled as an ingredient in Baker. Note that, within a recipe instance, the value of an ingredient may be overwritten by several value objects over time.
  • An entity is modelled in Baker as an ingredient of String type holding its unique key. Applying queries may produce entity attributes, which are again like other value objects represented using ingredients.

All names of messages and ingredients should match the ubiquitous language. It often helps to evolve the recipes and the glossary at the same time.

The commands and data lookup queries are usually implemented in services, which can be plain functions (“serverless”) or local or remote services within some bounded context. These services may perform side-effects, implement factories to create entities, protect aggregate boundaries, and persist data in repositories.

When integrating with external systems, Baker naturally leads implementing an anti-corruption layer by forcing to translate the system calls and callbacks to commands, queries, and domain events. If ingredients need to be transformed as part of such an anti-corruption layer, these transformations can be implemented as just another command: a Baker interaction which is a pure function.

Domain-Driven Design is one of the core tools that Cleverbase uses as a way to reduce complexity of its services. With Baker, Cleverbase found a compatible approach to keep applying DDD while maintaining a clear overview of the various processes in the company and thus keeping a high velocity in a dynamic environment full of changing requirements.

In the next post of the series, we will focus more on lessons learned along the way that made our use of Baker more effective over time.

Till next time!

If you need more information or have questions about Baker, feel free to open an issue on GitHub. This guest post was co-written with Nikola Kasev from ING Labs. If you want to know more about Cleverbase (we’re hiring!), contact Sander Dijkhuis.

--

--