In the Spaceteams DDD Workshop, we learned about Domain Driven Design (DDD) and its practical implementation. DDD is a software development approach driven by the business domain, and it has key concepts such as Domain Storytelling, Ubiquitous Language, and Bounded Contexts. The workshop focused on practical examples of DDD using xMolecules annotations and design by contract methodology, based on a banking example and a more detailed leasing ninja example. The workshop provided a hands-on experience to understand how architectural concepts can be expressed in code, ensuring the system behaves as expected and promoting easy-to-understand and maintainable code.
This article will focus on our latest workshop held by Henning Schwentner, an author, speaker, and a very well known figure in the DDD community, where he taught us about DDD in code.
DDD Concepts
Before we jump into DDD in code, let’s have a quick look at some of the concepts of DDD.
Ubiquitous Language: Ubiquitous language is a set of terminologies for defining different business processes or entities. These terminologies are used by the domain experts and therefore should also be used in the code, e.g. variable names, methods, classes, etc.
Bounded Contexts: Through domain storytelling, we get a visualisation of the domain. Afterwards, clear boundaries can be drawn to organise the domain, software and teams. These boundaries are known as bounded contexts. For example, if the business domain is about car leasing, we can draw boundaries of all the activities and entries related to sales in one context and all activities and entities related to risk management of the loan application into another domain and so on.
Domain Storytelling: It’s a way of transforming the domain knowledge into software applications which will tackle the business problems. During the process, domain experts and software developers come together and visualise the business process. This helps the developers to understand the domain and model the software accordingly.
Source: https://speakerdeck.com/hschwentner/
Building Blocks of DDD:
- Value Object: A Value Object is an immutable object defined solely by its value. Take, for instance, an IBAN for a bank account – a classic example of a value object.
- Entities: Entities are mutable objects with a distinct identity within the domain. They require identification to differentiate between instances and have a lifecycle. Consider a bank account with a unique account ID – an archetype of an entity.
- Aggregates: Aggregates are entities that can consist of multiple entities. They ensure consistency and integrity within their inner entities.
Source: https://speakerdeck.com/hschwentner/
- Repository: Repositories act as access points to aggregates, shielding the domain from the technical details of infrastructure. They facilitate the mapping of external data to entities and value objects, storing aggregates (e.g., in a database).
Source: https://speakerdeck.com/hschwentner/
- Domain Services: When business logic does not naturally fit within the context of any entities, value objects or aggregates, they are implemented as domain service.
Now that we have learned about the building blocks of DDD, let's dive into some examples that we worked on during the workshop. These examples will help us better understand and visualise the concepts of DDD in a practical way. We started with an example of using Domain-Driven Design (DDD) in the context of banking.
Banking Example Using xMolecules
In DDD, an entity is a representation of a unique and identifiable real-world object. In our banking example, an account perfectly fits the entity concept, possessing a unique identifier like an account number and a state indicated by its balance.
The account entity represents a unique and identifiable object in the banking domain, and its state is managed through the deposit and withdraw methods. These methods enforce domain rules, such as ensuring that transactions are not negative and that there are sufficient funds to cover withdrawals. By applying DDD principles, the banking example creates a software model that is aligned with the business domain and promotes code that is easy to understand and maintain.
Traditionally, it is common to check the account balance before making a withdrawal to ensure that there are sufficient funds available. However, inspired by the design by contract methodology, we chose a different approach by throwing an exception. This not only encapsulates error information for easier debugging, but also requires the caller to handle errors explicitly.
Design by Contract (DbC) is a software development methodology that emphasises defining clear and formal contracts between components of a software system. These contracts specify the obligations that each component has to others, and they are used to ensure that the system behaves as expected.
Example:
Source: https://speakerdeck.com/hschwentner/
The assertion assert amount <= balance() is a way of checking that the precondition of the withdraw() method is met. The precondition is that the amount to be withdrawn must be less than or equal to the account balance. If the assertion fails, an exception will be thrown.
- This is a way of specifying a contract between the withdraw() method and its caller. The contract says that the caller must not withdraw more money than is in the account. This contract helps to ensure that the system behaves as expected.
Moving forward with our banking example, we observed various annotations imported from xMolecules, which
- lets you to express architectural concepts in code
- gives you special labels and data types to describe how different parts of your software architecture fit together. These tools also automatically generate some basic code, making it easier to connect your architectural ideas with the actual code you write
In this example, the @Entity annotation indicates that the Account class is an entity. The @Identity annotation indicates that the id property is the primary key of the entity. The use of the annotation is a way of implementing DDD principles in code. It helps to ensure that the domain model is represented in a way that is consistent with DDD principles. Here are some of the benefits of using the annotation:
- Improved code readability: The annotation makes it clear to developers that a class is an entity. This can help to improve the readability of code.
- Reduced boilerplate code: The annotation can be used to reduce the amount of boilerplate code that is required to persist entities to a database.
- Increased maintainability: The use of the annotation can make code more maintainable by making it clear how entities are mapped to database tables.
Source: https://speakerdeck.com/hschwentner/
Leasing Ninja Example
After that we moved to a more detailed example, namely the leasing ninja example, an open source DDD real world example available here: leasingninja.io
The Domain:
Our domain for this example is the leasing of a car: a customer goes to a car dealer and requests a car, the sales person calculates the monthly rate and prepares a contract. After the customer signs the contract, the salesperson passes the contract to the risk department, which will vote on the contract. If everything looks good, the salesperson is informed and they can hand over the car keys.
For the example, we focus mainly on the “Sales” context and the “Risk Management” context.
After identifying our building blocks and bounded contexts using Domain Storytelling, we move to the code and:
- Make classes out of our work objects (nouns)
- Make methods/commands out of our activities (verbs)
The Code:
We started by looking at an overview of the Java implementation of the example, available here:
https://github.com/leasingninja/leasingninja-java-boundedcontexts-domainmodel
Then we also compared some parts of the code to other implementations that are interesting for us like using Typescript or using different ways like the functional programming implementation with F#.
The code of these example was very readable as it is structured with the DDD building blocks in mind and it also uses the xMolecules library for annotations.
Here are some code examples of DDD building blocks from the Sales Context. These building blocks are annotated as follows:
Car Value Object:
@ValueObject
public record Car(String car) {
public static Car of(String car) {
return new Car(car);
}
}
Contract Entity:
@Entity
public class Contract {
@Identity
private final ContractNumber number;
private final Customer lessee;
private final Car car;
private final Amount price;
private record Calculation(LeaseTerm leaseTerm, Interest interest, Amount installment) {}
private Optional<Calculation> calculation;
private Optional<SignDate> signDate;
...
}
Contract Repository:
@Repository
public interface Contracts {
Contract with(ContractNumber number);
void save(Contract contract);
}
FinancialCalculator Service (Simulates the infamous HP12c calculator):
@Service
public class FinancialCalculator {
public static double pmt(double n, double iInPercent, double pv, double fv, double s) {
double i = iInPercent / 100.0;
return pmtWithDecimalInterestRate(n, i, pv, fv, s);
}
...
}
Summary
At Spaceteams, we believe DDD is important and we think it is crucial that all our developers understand the business logic in our projects and be able to talk to stakeholders to achieve real value for the customer.
During the workshop, we learned about the advantages of Domain Driven Design (DDD). One of the key benefits of DDD is the clear separation between the domain and implementation details. This allows us to focus on understanding the domain and creating the right solution for the business.
In terms of workshop learnings, we delved into the practical aspects of DDD in code. We explored how to apply DDD annotations with xMolecules, which helps us express architectural concepts in code and connect our architectural ideas with the actual code we write. Additionally, we learned about design by contract, a methodology that emphasizes defining clear and formal contracts between components of a software system to ensure that the system behaves as expected.
Overall, the workshop was highly beneficial as it provided coding examples that helped us better understand the concepts of DDD. Our engineers had been eagerly awaiting this workshop as they had many DDD workshops before and they finally got to see the concepts in code and implementation.