What is Domain-Driven Design and why is it helpful?

Domain Driven Design (DDD) is an approach to software development, made popular by Eric Evans’ book, Domain Driven Design, published in 2003. It still receives a lot of attention today and is a highly respected tool in a lot of architects’ toolboxes. 

Why? Quite simply, DDD is about guiding engineers to focus on the business domain (e.g. the terms, rules, and business goals that business experts apply to a given problem), rather than technical concepts or frameworks, and to collaborate very closely with domain experts to find a shared language and model for the domain.

Over recent years, DDD has proved incredibly useful where software businesses scaled fast and developed a need for vertical cuts through their products, teams, and codebase. For example, the rise of microservices requires tech teams to define modular boundaries and assert which logic belongs to which service. DDD is also helpful in creating a product that is aligned with business priorities with fewer iterations, as it focuses on closing the gap between the code and the reality of the business needs.

At Taxdoo, we focus on the domains of Taxes & Accounting. We tend to deal with complex software where the limits of our understanding can soon become limits of our products and codebase. Luckily Taxdoo offers us to spend 10% of our time as ‘innovation time’ where we are encouraged to learn and experiment.

Making use of that, a few of us engineers and domain experts founded a book club, where we spend our innovation time reading and sharing what we’ve learned together.

There’s no such thing as too much collaboration.

With knowledge sharing, it can be tempting to have only software engineers at the table. We’re learning about a coding concept, so why involve other parties?

Having domain experts in the book club prevents engineers from becoming too technical or jumping into ivory tower discussions. 

At Taxdoo, we involve domain experts, called ‘Regulatory Analysts’, in our book club reviews and also our scrum ceremonies.

The result? We are using and constantly optimizing a shared language and mutual understanding about the rules and terms of our business that we use in our daily work and can be applied in our daily work. A huge bonus of involving domain experts so closely is that user stories are written by a domain expert, which reduces ambiguity and deviation. It took some time before both parties learned the terms used by the other, however, it has resulted in greater transparency and clearer requirements.

Business Process Model notation (BPMN): where analysis and implementation go hand in hand

Another interesting learning is that DDD discourages a separation between analysis and implementation.

Often, engineers collaborate with domain experts only until they think they understand the problem. Then they go and write the code, so that the implementation is hidden in the code and invisible to the domain experts. This then necessitates a step of translation, done by the developer, that often unconsciously changes domain logic or language. This drift in precision causes technical debt or bugs as the project progresses.

In Taxdoo, we use decision trees for our tax logic, and in the past, these trees were provided as diagrams by domain experts and then translated into code by us engineers. This approach was fine, however, we still weren’t quite accurate enough in communicating and understanding our requirements.

Last year we migrated this logic to BPMN, which is an industry-standard for business process modeling. The great thing about BPMN is that it combines the diagram and the implementation. This is done through an underlying XML layer. We can now collaborate by visually creating a diagram together and do not need an additional implementation that could impose the risk of misunderstandings or drift in language.

There’s still a long way to go, however moving to BPMN was a great start to close the gap between analysis and implementation.

Value Objects: capturing values in a transient state

DDD is a large concept that can be adopted on all levels of a company. That being said, not all companies have the capacity for this and in any case, it makes sense to experiment with concepts before applying them on a large scale. One application of the suggested code patterns that worked very well for us was the introduction of Value Objects.

In DDD, Value Objects represent values in a transient state, usually bound to one operation. They are distinct from entities, which are longer-living objects that have their own lifecycle and identity in the given context. In contrast, value objects don’t need any kind of identity, because two instances with the same values could be used interchangeably. 

Here’s an example where we introduced such an object and how it helped us:

VAT IDs

The VAT identification number is assigned to a company by the financial authorities and used for value-added tax (VAT) purposes.

In our world of B2B taxes, the VAT ID is a type of value that we need to process often. When a customer provides us with a VAT ID through a form or support requests, we usually receive it as a simple String. A first improvement to the object (to make it a more powerful part of the domain model) would be to restructure it in a way so that it yields more information about its purpose.

While this refactoring is not really providing a lot of additional logic, it already helps to fulfill the claim of DDD refactoring: “Refactoring towards a deeper insight of the domain”. By making this value a dedicated class and destructuring the characters into fields, the code becomes clearer about the contents of the VAT ID String, which is especially useful if you’ve never seen a VAT ID before.
We could now also add validation to make the object even more helpful.

For Taxdoo’s use case, there’s a little more to it: a VatId country code is usually the IsoCountry code of the country where the company has registered. However there are some edge cases. In Greece, the country code for VatIDs is “EL” instead of “GR” and Northern Ireland also has its own country code of “XI” even though its usual IsoCountry code would still be “GB” for Great Britain. 

To encapsulate that domain knowledge as part of our implementation, we can add a dedicated Getter that would translate the encoded country code into an IsoCountry:

By combining this domain knowledge we can provide an even more reliable parsing and validation. We know that the country code must either be castable to an IsoCountry or match one of the special cases mentioned above. If the provided country code prefix in the input String does not match these conditions, we know that the provided VAT ID is malformed:

In summary

Let’s recap: initially, we had a simple value, stored in a String. In the domain of tax evaluation, that value is often processed, parsed, and interpreted. We need to check for the validity of the VatId, we need to derive information and we need to send it across the network.

By making it a dedicated class, we strengthen our understanding of what a VatId is. If a new developer comes into our Taxes & Filings team and sees this code, the code represents the domain model that is useful when applied to our context. A lot of these principles could simply be regarded as “good OOP” or best practice, however in this case it helps us to understand the domain and share the attributes and capabilities of our models with the domain experts. 

In a real-world use case, there’s much more knowledge that can be contained in a VAT ID model inside of the context of taxes: Is the VAT ID valid? When was it registered? Note that depending on the context, the VAT ID could receive an identity with its own lifecycle (it’s even called ID!). In such a case, it would not be a value object anymore but would become an Entity, because we would store it with an identifier, so that we can track its lifecycle and add relationships to other objects, for example, a “Company” object.
Thinking about your objects and if they need a lifecycle in your domain will help you to obtain a deeper insight into your domain. This is a great tool from the DDD toolbox.

Layered Architecture

Our final example: the isolation of a domain layer in the code base. While we applied this as part of our interpretation of Clean Architecture, the isolation of a domain layer is part of the layered architecture that is suggested by the book.
We did this by creating a dedicated package for the domain logic and extensive use of Java Interfaces whenever we wrote code that was related to persistence or other infrastructural tasks. The domain package contains the long-living domain logic (e.g. applying the tax evaluation), while database transactions, API clients, or serializers are hidden behind interfaces with their implementations in a separate data package. The resulting package structure looks like this:

This separation allows us to make changes to the infrastructure (e.g. moving from RDS to DynamoDB) without touching the domain logic since we only need to provide another implementation of the respective interface. At the same time, we can also test our domain logic (which is the most important one to test) without worrying about the setup of a database or external service. It’s also easy to check or lint: No class in the domain package must ever contain an import from the data layer.

4 Tips for Getting Started with DDD in your Organisation

  • If you want to promote DDD or learn more about it in your company, involve domain experts from day one. They will prevent you from drifting into technicalities and you will collectively learn a shared language to discuss your daily collaboration.
  • Getting started with DDD works especially well when you work with serverless architecture (like Taxdoo) since architectural changes can be done faster compared to on-premise (which means that servers would be hosted physically by the software company itself, requiring time and financial resources to maintain their own infrastructure and server hardware) and the architecture is divided into smaller components. You’ll also spend less time on maintenance of non-domain-logic components since most of that is handled by the cloud provider.
  • Leave the larger and more complex patterns (for example Aggregates and Factories) for later and concentrate on the core idea. You don’t necessarily need to rewrite all code, to start your shift towards a more vivid domain model, but you can do that e.g. through the introduction of Value Objects.
  • If you haven’t done that, you can also start by isolating a domain layer first. Once that is done, you can apply more of the DDD patterns inside that layer, if it seems reasonable for you.

Good starting points for further information would be the “Blue Book” or the summary in the form of the DDD reference, both by Eric Evans. If you prefer to watch conference talks, I can also recommend checking out the DDD Europe Youtube Channel which contains a lot of talks around DDD concepts or their (technical) implementations.

Do you have experience with DDD? What were your takeaways from it? If you are new or are leveling up your DDD knowledge, we hope that you found this article helpful. Please feel free to re-share this post on social media or amongst your connections.

Finally, this blog post would not have been possible without the help of Paul, Maria, Alex, and Kevin. Thank you for the book club sessions that we had, and for your thoughts and inspiration!

In Taxdoo you’ll find the room and other motivated colleagues to invest time into new concepts and experiments. Join Taxdoo if you would like to participate!