Domain Driven Design applied

by Allard BuijzeJuly 28, 2009

In a recent project for Osix, we developed an application allowing visitors of a library to use the wireless Internet connection available there. Visitors can pay for the Internet access in two ways: an online payment, for example using a credit card or by deducting the amount from their library account. All user accounts, as well as the available products, library configurations and payments are managed from a single central application, called Digital Services Manager.

In this post, I will elaborate on how Domain Driven Design has helped us build a clean and maintainable application, mainly focusing on some technical and implementation choices that we have made.

DDD Background

Domain Driven Design is really about the way software is developed. Hence, it will say more about the way to get to an implementation than about the actual implementation itself. However, if you do it correctly, you will find some of the DDD principles back in your implementation.

The most important concept in DDD is the “Ubiquitous Language”: the language that is used within the domain by all people involved, such as domain expert (our customer), developers and stakeholders. Since this language is used by everybody, it is best to reflect that language directly –hence without translation– in your code. Since the language will contain both nouns and verbs, this means that you will end up with a Rich Domain Model.

Another really important principle of DDD is “make it explicit”. I can’t even recall how many times Eric Evans said that during the four day training he gave two weeks ago. This means that, for example, every time you come across some relationship between two values, you should assemble these two values and name the combination. This way, you make the relationship between these values explicit and named. This name should either come from the Ubiquitous Language or should be introduced as part of it. Yes, that means no single developer can make up a name for something without consulting the domain expert first.

How this reflects in the implementation

Most people I talk to are curious about how this reflects in the implementation. Some people even seem to reject DDD because it is not really all that different in the implementation. Of course it isn’t, at least, not on first sight.

In the OSIX domain, people can use wireless access points in Libraries to gain access to the Internet using their laptop. To do this, they need to register themselves in a central application, owned and maintained by OSIX. This application serves as a web shop where users can buy credits, it provides administrators to block or enable users and keeps a log of actual time spent online.

This means our domain model implementation contains classes like Library, User and InternetSession. This last one is DDD-implementation wise quite interesting.

We needed to log the time a user logged on and when he logged off again. Hence, our first name for this class was “LogEntry”. Technically, we are just talking about a giant Log with a lot of entries. However, DDD requires that everything is to be made explicit and named according to the Ubiquitous Language. So, we consulted the domain expert. He said he calls it Internet Sessions. A user starts an internet session, and an internet session is ended when a user logs of.

Does it change the implementation of the class much? Not really, except for some different method names, perhaps, nothing is really different. In the end, we’re still programming Java, and the same things need to be done. And if you’re used to Anemic Domain Models, you’ll probably have to get used to the fact that invariants are guarded inside the domain object itself, instead of a service.

So, why is this naming important? First of all, it allows your developers and domain expert to talk the same language, without the need of continuous translation. Secondly, new developers joining the project will easily pick up the language and recognize the concepts in the code. This really showed when we needed to change some developers. The new developer was quickly able to see which functionality had been implemented and which still needed some work. Finally, our client, who has a fair understanding of the Java language, was able to understand our implementation and give us direct feedback.

How about persistence?

Somehow, many people see Domain Driven Design and Rich Domain Model as the same thing. It’s simply not true. A Rich Domain Model is the logical consequence of a DDD project, since everything is made explicit. And making things explicit means providing names, properties and behavior to concepts.

Another assumption often made is that Rich Domain Model means “put all logic in the domain”. Which is also not true. It’s not called a model for nothing. Using a model means you consciously leave things out of your domain, because they are either not relevant or not practical in your implementation.

In the OSIX project, we assigned the responsibility of retrieving and persisting objects to the application layer. This means the application services call on the Repository implementations to retrieve or save objects.

Furthermore, we decided to keep all modifications to the state of Entities inside the scope of the Application Layer. In the implementation, this meant two things. First of all, the Transactional boundary is located on the service layer, meaning that entities leaving the Application Service are detached from the persistence context, meaning that changes will not be seen by the persistence manager and are ignored. Secondly, the service layer will never accept an entity as a parameter. If an entity is needed, the application service will retrieve it based on its identity, which is passed as a parameter instead.

Some of the Application Service interfaces methods look as follows:

InternetSession startSession(final LocalDateTime startTime,
                             final UserName userName,
                             final BranchCode branchCode,
                             final IpAddress ipAddress);

void endSession(final long id,
                final LocalDateTime endTime,
                final BranchCode branchCode);

All method parameters are value objects, and thus immutable, thread safe and the whole shebang. The startSession method returns en entity: InternetSession. If you wish to change the state of that object in a way that is useful to the application, you need to call another method (e.g. endSession) using the identity of the session, a long in this case. (Yes, in the case of “make it explicit”, SessionId would be a better name for it.)

The implementation of this application service is no rocket science either. It contains only little logic, since it delegates most of it to the domain objects themselves.

@Transactional
public void endSession(final long id,
                       final LocalDateTime endTime,
                       final BranchCode branchCode)
               throws NoSuchSessionException {

    InternetSession session = internetSessionRepository.loadSession(id);
    if (!session.getBranchCode().equals(branchCode)) {
        throw new NoSuchSessionException("This session belongs to another Branch");
    }

    if (session.isOpen()) {
        session.closeSession(endTime);
        internetSessionRepository.saveSession(session);
    }
}

Line 7 shows the actual InternetSession entity being retrieved from the repository. In Hibernate/JPA terms this means the entity is attached to the PersistenceContext, and all changes are observed by the persistence manager.

Line 8-10 show some very simple validation logic to prevent sessions from another Branch being closed accidentally.

Line 12-14 do the calls to the actual business logic involving the closing of a session.

You could state that the Application Service is responsible of the orchestration of business logic execution. It doesn’t say anything about what the business logic actually means (e.g. what does it it mean that a session is open?).

Conclusion

In the end, DDD doesn’t really drastically change the way most applications are built. The most important change is the actual naming of objects and methods and the location of the business logic. However, this seemingly subtle change can have a big effect on the maintainability of your application.

Domain Driven Design is mainly about making things explicit, and doing so in the Ubiquitous Language.