Building “The Better Store” an agile cloud-native ecommerce system on AWS — Part 2: Defining DDD Strategic Patterns

Bryce Cummock
11 min readOct 9, 2022

--

In Part One of Building the Better Store, I provided an introduction of an envisaged eCommerce website, and how Domain Driven Design (DDD) and its Strategic Patterns may be used to help compose a decoupled Microservices Architecture (MSA) that would promote its future change agility, scalability and fault-tolerance.

This article focuses on illustrating and using these patterns for design of The Better Store. Note this extends-from and makes reference to my TheBetterStore documentation at: https://docs.thebetterstore.net. Documentation at this website is continuously evolving.

Introducing Domain Driven Design

Domain Driven Design (DDD) is a methodology initially defined in 2003 in Eric Evan’s popular and well-received book ‘Domain-Driven Design: Tackling Complexity in the Heart of Software’ [1]. It is aimed at decomposing the complexity of a Problem Space (that is, a business’s functional domain/capabilities that are requiring development) into a
domain model comprised of one or more decoupled and cohesive Bounded Contexts; being sets of well-organised definitions of objects and business rules that share the same ubiquitous language understood by both business stakeholders and technical staff, to offer easier interpretation and design of software. These Bounded Contexts define the problem’s Solution Space.

While DDD predates the advent of microservices by almost 10 years, its goals and defined patterns for designing systems as cohesive, decoupled and collaborative bounded contexts are strongly aligned with microservice architecture design principles, for implementing cohesive and decoupled services that realize advantages of change agility, resilience and scalability within cloud environments. As such, DDD has become a popular reference today when discussing microservice architectures.

Domain-driven design is aimed at promoting system design quality via the following methods:

A. Using Strategic Patterns to:​
1. Distil the Problem Space, i.e. an organisation’s business capabilities, to identify features important to the business by:

  • Using ‘Knowledge Crunching’ methods to extract relevant information and help identify and decompose the problem space into separate manageable subdomains.
  • Defining a Shared/Ubiquitous Language (UL) for each subdomain that is understood by both business and technical stakeholders, which should subsequently be used for documentation and code artefacts (e.g. for the names of classes and their methods and attributes).

2. Produce a Solution Space comprising Bounded Contexts to demarcate and model solutions for identified subdomains. This should include a Context Map that defines relationships between the bounded contexts.

For example (with credit to Millett & Tune [3]):

Figure 1: Illustrating DDD Strategic Patterns for defining a Problem Space
Figure 2: DDD patterns for a corresponding Solution Space

B. Using Tactical Patterns to:​

  1. Create an effective Object-Oriented domain model for each Bounded Context, using defined patterns such as Entity, Aggregate, Value Object, Service, Factory and Repository.
  2. Illustrate emerging patterns of domain events and event sourcing.

This article will next focus on application of Strategic Patterns for design of The Better Store. The application of tactical patterns will be the focus in my next article: Part 3: Defining DDD Tactical Patterns.

The Better Store & The Problem Space

As an initial step in the design of The Better Store, we would expect one or more appointed business domain experts to collaborate with the development team in ‘Knowledge Crunching’ sessions, using popular techniques such as ‘Event Storming’ for fast sharing and discussion of ideas for desired functional behaviour. Event Storming has been touted as having much success in recent years, for sharing ideas of system behaviour and scope between both technical and non-technical people [4].

The output of Event Storming workshops are highly-visual diagrams conveying envisaged business contexts and behaviours. From here, main use cases may be chosen that will become part of the ‘Core Domain’ for implementation of a Minimal Viable Product (MVP). More explicit specifications for these can then be defined using tools such as:

1. Behaviour-Driven Development (BDD) specifications to focus on the behaviour of the main use cases oriented around specific scenarios.
2. Class Responsibility Collaborator (CRC) cards to define potential structural elements from these, and the interactions between them.

The application of these techniques will be illustrated at a very high-level below for an MVP definition of The Better Store. Further reading is recommended for those that wish to learn more of the techniques; the scope for each of these, in particular Event Storming and BDD is large!

NB: the methodologies chosen here have been selected as examples for The Better Store as a new application. Other techniques would undoubtedly also come into play for other scenarios; for example, exploring published models for similar applications, or referencing existing documentation for a current application that is to be enhanced or refactored.

A. Identifying System Behaviour Using Event Storming

Alberto Brandolini first introduced this methodology in his book Introducing Event Storming [3], whereby analysis of a business process involves bringing together the development team and the right business process expert(s) from applicable “knowledge silos” within the business as participants in collaboration workshops. Within the workshops, participants are requested by the workshop mediator to use different colour-coded sticky notes to model a flow of domain events, commands and external systems over time (from left to right) i.e. temporal modelling on a clear wall space. Online collaboration tools such as Miro are also available to simulate this activity for teams where participants are in remote locations.

Event storming is often performed in a number of phases as defined and controlled by the mediator as appropriate for the system and/or goal; for example for a high-level design of The Better Store we will derive these from Brandolini’s ‘Big Picture Workshop’ configuration to use:

Phase 1. Chaotic Exploration

  • All participants add orange notes to represent expected domain events (verbs in the past tense), and lilac notes to represent questions or unknowns (specific discussions should be kept short at this time; our focus here is coverage).

Phase 2. Timeline Enforcement

  • Events are rearranged to restrict them to a single timeline. This should result in further discussions and help identify gaps or unknowns.

Phase 3. Commands, People and Systems

  • Blue notes are added to represent commands, which may be called by external users or systems to generate the events.
  • Pink notes are then added to represent external systems that may be used.
  • Finally, yellow notes with a User icon are added to represent people that interact with the system.

Phase 4. Explicit Walkthrough

  • Different narrators take the lead for describing the behaviour for different portions of the system, for further review by other participants.

Phase 5. Identifying Aggregates

  • We can use yellow notes to denote potential ‘Aggregates’; that is, system entities that have their own identity id, sub-elements and transactional lifecycle (we will be discussing these more in the next Tactical Patterns article). An example is Order, which will be required to have its own unique id for storage within a database, alongside its selected product items as attributes (which would also be persisted when an order is created or updated in a data store).

Phase 6. Problems and Opportunities

  • Additional time for participants to add further questions (lilac notes), or opportunities (green notes).

Phase 7. Wrap up

  • Ensure photos of the board are taken (and notes are kept if hard to ready from photos), to allow its referencing for analysis and modelling later.

The following provides an example output from a remotely-run (using Miro) Event Storming session for The Better Store.

Figure 3: Event Storming sample output for The Better Store

B. Defining Functional Behaviour with Behaviour Driven Development (BDD) Specifications

BDD is a software development process based on Test Driven Development (TDD), which focuses on defining explicitly functional scenarios and required behaviour, using its own ‘GWT’ (Given, When, Then) specification
language. This format is also intended to provide a language that is understood by both business and technical stakeholders for defining requirements, oriented around user stories, or ‘features’. Examples for “The Better Store” generated as BDD specifications may include:

Using this method of capturing requirements removes the ambiguity that traditional requirements documentation can result in while also focusing on the domain language. The generation of these can also help with formulation of a subdomain’s Ubiquitous Language [2].

C. Defining Functional Components with Class Responsibility Collaborator (CRC) cards

CRC Card modelling is an object-oriented technique that involves identifying the main system actors, e.g. from the Aggregates first deduced during Event Storming or entities noted in the BDD specifications above, and representing these visually for easy collaboration on separate cards.
Each CRC card should capture the following:

  1. A class name, which represents a known concept within the domain and is easily understood by business and technical members (this will go into our Ubiquitous Language).
  2. Class responsibilities.
  3. Associated classes. A class often does not have sufficient information to fulfil its responsibilities and must collaborate with other classes to complete their task. Such collaboration may be either: i. a request for information from another class, or ii. a command to perform an action.

e.g.

Figure 4: CRC card examples for The Better Store

Distilling the problem space​ into subdomains

The above exercises, in particular Event Storming and CRC cards assist us with demarcating functional requirements into separate subdomains, identifying dependencies between them, and an initial Ubiquitous Language for each. As a final step in defining our problem space, we would like to further categorise these in order of importance to our business. The outcome of this exercise is to prioritise functionality for development focus, to ensure that tasks that provide the most competitive advantage receive the most attention.

Core Subdomains
These cover the most important part of the business that provides its competitive advantage. Identification of the core domain(s) here helps provide clarity of the software that should receive the greatest development focus. For The Better Store these have been identified as:

Core domains include ProductCatalogue and Order

Supporting Subdomains
These provide supporting functions to the core domains. If possible, Commercial Off The Shelf (COTS) products should also be used.

Generic Subdomains
Generic subdomains provide common functionality that are not core to the business and could also be provided by COTS software (again freeing-up developers to focus on the core areas).

The following diagram summarises the subdomains identified for our problem space, and their dependencies:

Figure 5: High-level domain model

Defining the Solution Space​

The Solution Space provides a model for realizing the needs of the requirements given in the Problem Space, for example by defining appropriate Bounded Contexts (BC’s) for each subdomain to implement. Each bounded context is also provided with its own Ubiquitous
Language; much of which should have been defined during analysis of the Problem Space for the belonging subdomains. In this way each bounded context is kept cohesive to a specific functional area.

Following the modelling of Bounded Contexts, the solution space also defines Context Mapping, using Integration Patterns to define the collaboration relationships between these. This is an important output which will highlight the degree of decoupling between services, and the shared knowledge required by the teams that own them.

DDD’s Integration Patterns are described below [1, 10]:

Figure 6: DDD integration patterns

where the patterns may be categorised and described below. Context Mapping annotations are also provided below; their usage is illustrated in a sample context mapping diagram for The Better Store that follows.

  1. Symmetric Patterns: where 2 BC’s have related interdependencies.
  • Separate Ways; BC’s are independent with no relationships between them. Teams can work at their own pace. This is an ideal scenario!
  • Partnership; 2 BC’s are mutually dependent on each-other; i.e., they are tightly-coupled. Teams need to know the business models and UL of the other team. Changes to 1 BC need to be coordinated between teams. These are an anti-pattern and should be avoided where possible.
  • Shared Kernel; A move to demarcate shared models used between BC’s. e.g., as a separate shared library and UL. This should be kept to a minimal number of contexts.

2. Asymmetric Patterns: where one BC is dependent on another. The dependent BC is termed Downstream (D), whereas the provider/host is termed Upstream (U). BC’s in D hence have knowledge of models in U BC’s.

  • Customer-Supplier; An upstream BC exposes models specifically for the needs of a downstream BC; i.e., in a client/host relationship.
  • Conformist; An upstream BC exposes models with no regards to any downstream BC. The downstream BC conforms to the upstream BC’s models.
  • Anti-Corruption Layer (ACL); where a downstream BC is NOT conformist; an isolated transformation layer is used to protect the downstream BC from corruption, i.e. from using the upstream domain’s model. The ACL only has the knowledge of models from U and D to perform necessary mappings from U’s model to the downstream BC (D).

One-to-many Patterns

  • Open-Host Service (OHS); the upstream provider/OHS offers common services to other BC’s. The downstream BC’s may choose to either conform to U, or use an ACL.
  • Published Language (PL); The OHS provides a common language accepted by a downstream BC. These are denoted as OHS | PL for Upstream in a context mapping diagram. OpenAPI specifications for REST API’s are an example of these.

Note we want to avoid the ‘Big Ball of Mud’; this is the described anti-pattern which often results from unchecked growth of a monolith over time, where without practices code can become unstructured and very hard to extend and maintain.

A context mapping diagram for The Better Store may be modelled as:

Figure 7: A context map for The Better Store

Next Steps: Creating a Model Driven Design

The strategic patterns shown above have provided a high-level design of expected service decomposition as Bounded Contexts and the relations between them. Key outputs are:

  1. Decomposition of bounded contexts; with each having its own Ubiquitous Language and business rules definitions.
  2. Context mapping, to define required relationships between them, with maximum decoupling being in favour.

As can be seen, DDD is a broad and involved methodology, and there are many tools available for decomposing the problem space to define the required outputs. Its full application may be better recommended for working with large and complex domains that are difficult to manage with other techniques [11], however I believe many of the discussed methodologies here such as Event Storming, CRC cards, Ubiquitous Language definitions and references to decoupled integration patterns would prove valuable for defining microservice architectures for less complex domains also.

Coming next in Part Three: Defining DDD Tactical Patterns. This will explore the more technical and object-oriented modelling of bounded contexts to complete the solution space. We will also be drilling into the tactical patterns for the realization of cohesive and decoupled microservices for The Better Store.

References

  1. “Domain Driven Design, Tackling Complexity in the Heart of Software”, Evans, E., Addison-Wesley (2003)
  2. “Patterns, Principles, and Practices of Domain-Driven Design”, Millett & Tune, Wiley & Sons (2015)
  3. “Introducing Event Storming”, Brandolini, A., LeanPub.com (2021)
  4. “Event Storming: Collaborative Learning for Complex Domains”, Rayner, P., Talk in Saturn (2017), https://www.youtube.com/watch?v=vf6xoi2d9VE
  5. “Practical Event-Driven Microservices Architecture”, Rocha, H., Apress (2022)
  6. “Open Agile Architecture”, The Open Group, https://pubs.opengroup.org/architecture/o-aa-standard/index.html (2020)
  7. “Implementing Domain Driven Design”, Vernon, V., Addison-Wesley (2013)
  8. “Building Microservices”, Newman, S., O’Reilly (2015)
  9. “Big Ball of Mud”, Foote & Yoder (University of Illinois), web (1999)
  10. “Domain Driven Design & Microservices for Architects”, Sakhuja, R., Udemy (2021)
  11. “Microsoft Application Architecture Guide, 2nd ed”, Microsoft, web (2013)

Disclaimer: The views and opinions expressed in this article are those of the author only.

--

--

Bryce Cummock

A Cloud-native solution designer and consultant, specialising in AWS implementations for both start-ups and enterprise organisations.