Trifork Blog

Domain Driven Design applied

July 28th, 2009 by
|

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.

7 Responses

  1. July 29, 2009 at 17:52 by Oliver Gierke

    Cool sample! One thing I’d like to ask is why you insist on not accepting any entities at the service layer. I tend to see the following consequences:

    1. Abstraction of the service layer decreases. You get a rather technical un-domain-driven interface of the service layer by doing that.
    2. How do you expose persisting the domain object then? It needs transactional boundary, so I tend to expose a save(..) method on the service.

    I prefer to let the clients of the service layer use the rich methods of the entities to do more than getters and setters and hand it to the service layer to apply persistence. Let me give an example you can probably follw very easily. You have Accounts and Users that can be linked to each other. How would you expose this functionality to the clients of a service? I’d take the route of offering an Account.link(User user) method, letting that method check certain domain constraints and let this be invoked by the client and hand the account back to the service to persist the operation. If I got you right, you rather offer an link(Long accountId, Long userId) via the service and handle the Account.link(User) call inside the service, right? I don’t see how this makes it more accesible to the clients.

    Really look forward to hear your opinions on that!

    Regards,
    Ollie

  2. July 30, 2009 at 10:27 by Christoph Rueger

    Hi Allard,
    this is very interesting post dealing with questions I ask myself over and over again.
    Especially your section about persistence, what to put in the DataAccessLayer and what to put in the Service layer.

    Looking forward to more articles like that.

    Christoph

  3. August 3, 2009 at 16:05 by Allard Buijze

    Christoph,

    thanks for your comment. You might like my other blog entries about DDD in that case too: http://www.gridshore.nl/category/ddd/

    Regards,

    Allard

  4. August 3, 2009 at 20:09 by Allard Buijze

    Oliver,

    thanks for your reply. I’m not sure I really understand what you mean in your first point. To me, having a “save” method on your service layer is rather technical. Instead, I like to see my application (aka service) layers as the functional boundary of an application. Everything your application is supposed to do, is done within those boundaries. That means that any entities that cross that boundary are detached from any transaction or persistence context and are considered “lost”. You could see the application services as use case controllers.

    Your example of linking an account to a user, would have a method like the following on your service layer: public void linkAccount(AccountId accountId, UserId userId), quite similar to what you said.

    Inside, you would retrieve the corresponding entities from the repository and use the link method on them.

    It doesn’t per-se make it more accessible to a client, but it is the cleanest way to force some form of separation between the application and ui layers. I am not saying this *the* way to do it. It is just a way we did it, and for our application, with relatively few use cases, these simple rules have helped our (relatively small, but flexible) team keep a sound and nicely decoupled codebase in the months that we have been developing this application. We didn’t have any discussions as of where to put certain logic. Everything is clear and well confined within a predefined boundary.

    Of course, this approach has some drawbacks if your application has hundreds of different use cases. But still, with a nice modular approach, you should still be able to keep a relatively good overview, even in this case.

    About your second option. Persisting domain objects is not the responsibility of the service layer. Repositories are responsible for that.

    Hope that clarifies my point of view.

  5. August 4, 2009 at 17:40 by Oliver Gierke

    Hey Allard,

    thanks for your response. I’ll try to get it down one by one.

    The save method. This actually is a combination of what you might express as create(Entity entity) and update(Entity entity) method combination. I think we can agree on the client having a necessity to create and update entitys it might have edited via the service interface, can’t we? I tend to merge this into one method to make the distincion not to bother the client. Although in very simple cases a call to save might result in a simple delegate to a repository, but in most cases there are additional steps do be done.

    Your linkAccount method makes sense as you treat the IDs of entities as value objects. In general we hardly ever need more than a Long so that the additional abstraction does not add much value. I agree that this way the service interface has a more “domainic” surface but the benefit seems rather small to me. In contrast, stripped down to the Long variant, the interface becomes much more technical that if you’d handed out the entity and let the client to the work on the entity itself through domain functionality e.g. account.link(user). To me the service interface you suggest leads to a very procedural way of programming at the client side whereas mine results in a rather thin layer (and thus is much more like a facade) above the multiple repositories. This seconds your thoughts on building modular interfaces.

    Regarding your last paragraph: How do you deal with services and repositories in combination then? Do you use them side-by-side from a clients point of view (suppose the mentioned (create, update, getAllFoo) functionality clients will need to access?

    Very cool to discuss this stuff here. I really like shading light from different angles on this topic :).

    Regards,
    Ollie

  6. August 6, 2009 at 09:35 by Allard Buijze

    Hi Oliver,

    first let me make one thing clear. I don’t think there is anything wrong with the approach to having the “domain operations” called by the UI layer. It’s just that in our application we chose to use the Use Cases as operations in the service layer. That does not mean that there is one service method per domain method. This approach leads to very thin client layers that typically only need to do a single call, as a user interface usually deals with one use case at a time. This also makes transaction management very easy. Just annotate the service methods with Spring’s @Transactional, and you’re done. Persistence is managed for you.

    As I understand, you prefer to have the UI Controller in charge, and I will not be the one to say that is the wrong way to go. I am sure that, in some circumstances (e.g. when the amount of use cases is simply too high), your approach leads to easier to maintain code than mine. But in our case, and you’ll just have to trust me on that, since I am not allowed to publish the codebase, it just wasn’t the most suitable approach.

    And to answer you “service/repository side-by-side question”: no, only the service layer accesses the repositories. The service layer is responsible for checking security rules, retrieving entities, calling the right methods on them, and then persisting their state again. Of course, most of these rules are delegated to more specific implementations (e.g. security strategy).

    Anyway, if you have written anything about the approach you use in you applications, I am eager to read about it.

    Regards,

    Allard

  7. […] For those people reading this blog item and think: “What is DDD?“. DDD is an abbreviation for Domain Driven Design. We have been writing about DDD before on this blog: “The misunderstanding of domain driven design“, “Domain Driven Design applied“. […]