Sitemap

A Soft Introduction to Domain-Driven Design: From Theory to Java Code Implementation - Part 1

8 min readAug 19, 2023
Press enter or click to view image in full size

Before joining my current company, Domain-Driven Design (DDD) used to be a subject that required preparation before interviews, just in case the interviewer decided to ask me about DDD. Fast forward to today, it’s an active part of my daily work. With every task, I find myself uncovering new insights thanks to DDD’s principles.

Initially, DDD seemed complex and challenging to grasp. Terms like ‘Aggregates,’ ‘Entities,’ and the fancy ‘Ubiquitous Language’ felt like barriers. But now, these terms are bridges that help us understand one another clearly. Ubiquitous Language? It’s just a way to say, ‘ we all understand each other.’

Domain-Driven Design (DDD) is like a solution for project chaos. It’s a toolkit that brings order to development. Instead of things being messy, DDD helps organise everything. It’s like a tool that makes business goals, technical work, and user needs fit together nicely. With DDD, things become more organised and structured, creating software that works well, even in complex situations.

This article is divided into two parts. The first part might not be the most exciting for everyone as it covers theory and DDD terminology.I will keep this part short as much as possible. The second part, on the other hand, gets hands-on with a real-world Java coding example. Let’s roll.

Part 1. Theory and Terminology

Domain-Driven Design (DDD) is an approach to software development that emphasises understanding and modelling the real-world problem domain within the software design process. It’s a methodology that aims to align the software closely with the complexities and intricacies of the business domain it serves.

At its core, DDD focuses on effective communication and collaboration between technical teams and domain experts. It provides a set of principles, patterns, and guidelines to structure and organise software projects, making them more maintainable, adaptable, and reflective of the underlying business requirements.

I will concentrate on explaining seven key terms that play a crucial role in understanding Domain-Driven Design (DDD): Ubiquitous Language, Bounded Contexts, Entities, Value Objects, Aggregates, Services, and Repositories. Before diving into the details of each concept, let’s take a brief overview of what they essentially are and the basic advice they provide

  1. Ubiquitous Language: DDD advises to use the same words for things in your program that you use when talking about them with others. When you’re talking about code or having a tea/coffee chat, you’re speaking the same language. This helps everyone understand and work together.
Press enter or click to view image in full size
  1. Bounded Contexts: Think of Bounded Contexts as different parts of a big library. Each section has its own type of books and special words to describe them, so people can find what they want easily. Use Bounded Contexts to keep things clear and separate. Make sure each area has its own way of talking about things, so everyone knows what’s going on.
  2. Entities: Think of entities as the main characters in a story. They’re like special characters with their own names, personalities, behaviours, and they can change over time. Each one has its own identity and can do different things as the story goes on.
  3. Value Objects: Value objects are special details that describe important things, like colors or prices. One important thing to remember is that value objects are usually immutable, which means once you create them, they don’t change. They stay the same, like a snapshot, to make sure everything is clear and reliable.
  4. Aggregates: Imagine putting things together in a group that make sense. This group is like a team and helps us understand things better.
Press enter or click to view image in full size
  1. Services: Think of helpers that can do special jobs for you. They don’t have their own lives, but they can help you get things done.
  2. Repositories: They are bridges between your application’s code and the data storage layer, like a JPA and JMS. They are facilitators data abstraction and management. (Generally Interfaces).

Ubiquitous Language is like a secret code in Domain-Driven Design (DDD) that makes communication super clear between techies and experts in a field. It’s about using the same words for stuff in the software as experts use in real life. So, when you’re talking about code or having a tea/coffee chat, you’re speaking the same language. This makes it way easier to understand each other, avoids confusion, and helps teams work together smoothly. It’s like having a translator that makes sure everyone is on the same page.

For example, think of making a fancy banking app. With Ubiquitous Language, the names you give to classes, methods, and things like account balances match the real banking terms like “Account,” “Transaction,” and “Balance.” This way, when developers and banking experts chat, they’re speaking the same lingo. A class named “Account” really is about bank accounts, and methods like “processTransaction” and variables like “accountBalance” make sense to everyone. It’s like turning a foreign language into something everyone understands, which helps the tech folks and the financial folks work together like a well-oiled machine.

Bounded Contexts: Explicitly defined contexts that encapsulate specific parts of the domain and provide clear boundaries for concepts and terms. Basically, DDD advise to define clear boundaries between different bounded contexts. This separation prevents confusion and ensures that each context can evolve independently.

Let’s say, we’re developing an e-commerce platform with two bounded contexts: “Product Catalog” and “Order Management.”

In the “Product Catalog” context:

  • Focus on managing product information like names, descriptions, and prices.
  • Define classes and methods specifically tailored to product-related operations.
  • Use terminology like “Product,” “Catalog,” and “Price” to reflect the context’s domain.

In the “Order Management” context:

  • Concentrate on handling customer orders, payments, and fulfillment.
  • Define classes and methods that deal with order processing and tracking.
  • Use terms like “Order,” “Payment,” and “Fulfillment” to match the context’s language.

By maintaining these clear boundaries, the “Product Catalog” context remains separate from the “Order Management” context. Each context can evolve independently, and the codebase remains organized and focused on its specific domain concerns.

Entities are the most important concept in Domain-Driven Design (DDD). They represent distinct and identifiable objects with a unique identity that runs through time and various states. Unlike simple data objects, entities have a lifecycle, and changes to their attributes are tracked over time. An entity’s identity remains constant even if its attributes change.

Entities are like real-world things that are special and have their own life stories. Think of them as important items in a game or a story. For example, in a banking app, you could have things like “Accounts,” “Transactions,” and “Customers” as entities. Account has an identity, state and life cycle. it’s not just any account, it’s one with its own identity, it has some important details (like how much money it has), and it goes through different phases (like when it’s opened, used, or closed). These entities help us keep track of special things and understand how they change over time.

Value Objects are an essential building block in DDD. They represent objects that are immutable, meaning their state cannot be changed once they’re created. Unlike entities, value objects don’t have a distinct identity; they are defined solely by their attributes. They are used to encapsulate concepts in your domain that don’t change their meaning based on identity, and they help in modelling the behaviours and characteristics of those concepts.

Value objects don’t have their own lifecycle, state or identity; instead, they are characterised solely by their attributes and properties.

For instance, consider the “Address” of an Account holder. This “Address” doesn’t have a unique identity by itself, but it contributes to defining the Account holder’s information. Since it’s solely based on its attributes (like street, city, and postal code), it’s a prime candidate for being a value object.

Another example could be the “Amount” in a transaction. Each amount doesn’t need its own identity; rather, it’s just a number associated with the transaction. Therefore, “Amount” could be modelled as a value object.

Choosing Between Entity and Value Object for an Objective

Distinguishing between Entities and Value Objects in Domain-Driven Design (DDD) is important because they serve different roles and have different characteristics in your domain model. Here are some guidelines to help you differentiate between them:

Press enter or click to view image in full size

**BTW these are guidelines, and the final decision should be based on the unique requirements of your domain.

Aggregates:

An aggregate is a fundamental concept in Domain-Driven Design (DDD) that groups together related entities and value objects into a single unit, forming a boundary for transactional consistency and enforcing business rules. Aggregates are a way to manage complexity and ensure that changes to the data maintain the integrity of the domain.

Key Characteristics of Aggregates; Boundary and Transactional Consistency

Boundary: Aggregates define a boundary within which business rules and invariants are enforced. The internal components (entities and value objects) should be consistent and valid when viewed from outside the aggregate.

Transactional Consistency: All changes to the data within an aggregate are treated as a single unit of work. Transactions are atomic, and changes within the aggregate are persisted or rolled back together to ensure consistency. In other words, all changes to the data within an aggregate either succeed together or fail together. This guarantees that the data remains in a valid and consistent state, even in the face of failures or interruptions.

Repositories are abstractions that provide methods to access and store aggregates, isolating the domain from the data storage details. They play a significant role in managing the storage and retrieval of aggregates from the underlying data storage, such as a JPA, JMS. Repositories act as a bridge between the domain model and the data access layer, providing a way to store and retrieve aggregates while encapsulating the underlying storage details.

Repositories abstract the data access and storage details from the domain model. They provide a higher-level interface for working with aggregates without exposing the specifics of how data is persisted. They are mostly defined as Interface. Implementation of this interface will be somewhere else (I will explain in Structures section) In other words, aggregates or entities or any domain member will be unaware about how repositories will be implemented.

Services: Operations or behaviors that do not naturally fit within the scope of an entity or value object and are used to perform specific actions. It is a concept used to represent operations or behaviors that don’t naturally fit within the boundaries of a single entity or aggregate. Services are a way to encapsulate domain logic that involves multiple entities or aggregates, or logic that doesn’t belong to a specific entity at all. Services are often used to model operations, calculations, validations, and other behaviors that span multiple parts of the domain.

In conclusion, understanding these foundational concepts of Domain-Driven Design (DDD) empowers us to create more effective and maintainable software solutions. By embracing the Ubiquitous Language, defining clear Bounded Contexts, identifying Entities and Value Objects, forming well-structured Aggregates, utilizing Services, and mastering Repositories, we pave the way for building robust and domain-centric applications.

To bring these concepts to life, let’s delve into a real-life example in the world of Java programming. We’ll explore how these principles are applied to a practical scenario, demonstrating how DDD can lead to elegant and efficient solutions in software development. So, stay tuned as we dive into the exciting world of DDD in action through Java.

--

--

Responses (1)