cult3

What are Modules in Domain Driven Design?

Dec 10, 2014

Table of contents:

  1. What are Modules?
  2. What is the difference between a Module and a Bounded Context?
  3. Modules in different layers
  4. Modules as separate Composer Packages
  5. Conclusion

Most programming language have the concept of self contained modules in one form or another. For example, PHP have the concepts of Namespacing.

Namespaces allow you to keep related code under the same “bucket” and prevents name collisions between classes from different vendors.

Modules are an important concept in Domain Driven Design for a couple of reasons. Firstly they act as purely a form of code organisation, but secondly they also represent an important concept of the structure of the Ubiquitous Language.

In today’s article we’re going to be looking at the importance of Modules in Domain Driven Design, how to think about them and how to extract the important concepts from the Ubiquitous Language.

What are Modules?

A Module serves as a container for a specific set of classes of your application. In PHP this would be a specific namespace that contains all of the related classes for that specific concept.

The code within a module should be highly cohesive and there should be low coupling between classes of different modules.

There are 4 important things to remember when thinking about Modules.

Derived from the Ubiquitous Language

The name of the Module should be derived directly from the Ubiquitous Language and should reflect an important concept from the Domain. Any member of the team should be able to tell you what role and responsibilities any particular Module should possess, given it’s name.

For example, if an important concept to your Organisation is Identity it makes sense to name the Module that deals with this functionality Identity.

The Ubiquitous Language of the project should guide and instruct the naming of Entities, Value Objects and Services and so it is no different for Modules. A Module is just as an important concept as any Entity of the application.

So instead of grouping the code around users under a Users namespace, instead use the word that is directly derived from the Ubiquitous Language, so anyone on the team will understand what this specific Module is used for.

Contains one or more cohesive Aggregates

A Module will typically contain one, or sometimes multiple Aggregates that are highly cohesive. If you have multiple Aggregates in a Module, but one is not cohesive with the others, it’s time to break it out into its own Module.

For example, in a Discussion Module you might have an Aggregate named Thread. The Thread Aggregate would act as the controller for the Entities and Value Objects that are required for a discussion thread.

Discussions are usually organised into groups or categories of a specific topic or genre. Whilst discussions are related to groups, it makes sense to move the Group Aggregate into it’s own Module because we are mixing two important, yet different concepts of the Domain into one Module.

Organised by the Domain

Modules should be organised by concepts from the Domain, and not by the type of class. For example, don’t group all of your Aggregates, Services and Factories into different Modules based upon the type of class. Instead, each Module should have the appropriate classes to model the concept and functionality of that specific aspect of the Domain.

Typically a lot of projects will be organised in the following structure:

  • Vendor/Models
  • Vendor/Repositories
  • Vendor/Services
  • Vendor/Factories

However you shouldn’t organise your code by it’s technical type. Instead you should group together related code that represents a concept of the Domain.

An alternative organisation could be:

  • Identity- User
  • UserRepository
  • Email
  • EmailIsUnique

Modules should be loosely coupled

Models should be loosely coupled as standalone aspects of your project. You should be able to abstract the code out of your project and test it in isolation without having to cut ties or pull a tangle of interrelated knots with your other modules.

Typically you won’t have a pure separation between Modules. The very nature of Domain Driven Design means that there will be relationships between Modules.

However you should aim to distill the relationship between Modules to only those that directly model concepts from the Domain.

You should also aim to reduce the coupling of related Modules by making the dependency between two Modules unidirectional. For example a Thread belongs to a Group, but a Group doesn’t really care about the existence of a Thread.

What is the difference between a Module and a Bounded Context?

A couple of weeks ago we looked at Bounded Contexts and how they act as a boundary around certain components of our application. The majority of large applications will have multiple Bounded Contexts that might involve different third-party services or legacy systems.

Most organisations will have such a large Domain Model it is important to split it into multiple Bounded Contexts. This keeps the context and integrity of the internal concepts inside the Bounded Context consistent and unified.

Inside the Bounded Context, each concept should be broken down into Modules. A Module will represent the related classes of a specific concept of that Bounded Context.

So Modules represent important concepts inside of Bounded Contexts.

Modules in different layers

So far we’ve looked at Modules in the Domain layer of an application, but Domain Driven applications will also have Application and Infrastructure layers.

Again, there is no hard or fast rule with how you should organise your code. However, I think organising this code into the same named Modules makes it easier to see how code is related across the different layers of the application.

For example, you might have a UserRepository interface in the Domain Identity Module. This interface requires a concrete implementation UserDoctrineRepository that would sit in the Identity namespace of the Infrastructure layer.

Modules as separate Composer Packages

Over the last couple of years, PHP as a language as broadly adopted Composer as it’s package manager of choice.

When you break a project up into individual Modules, theoretically you could actually make those Modules individual packages that can be pulled into the application as dependencies.

This is the ultimate separation Nirvana, but it probably works better in theory than it does in practice.

Typically there will be relationships between Modules and so physically separating code into individual packages will often be more trouble than its worth.

Conclusion

Modularity is an important concept that transcends across all programming languages. Using Modules in a Domain Driven Design application will help you organise your code, keep related things together and nudge you along the path towards low coupling.

You don’t have to go all out and separate your code into individual Composer packages, but thinking of your project as a series of modules will certainly reduce the cognitive overhead of tackling a big project.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.