Inside Paulo Abrantes' head
[ start | index | login or register ]
start > 2007-01-19 > 1

Software Developing: More About Domain Driven Design

Created by pabrantes. Last edited by pabrantes, one year and 215 days ago. Viewed 2,036 times. #7
[diff] [history] [edit] [rdf]
labels
attachments
lib-example1.png (4624)
lib-example2.png (12484)
lib-example3.png (18768)
lib-example4.png (22365)

Software Developing: More About Domain Driven Design

The idea of this post is to explore ideas that have been previously mentioned on >>Software Developing: Domain Driven Design and may were seen as unclear thoughts. In that post, I've answered jff with an example of a Library Management System, we'll look in that example in detail.

Before going any further I want to clarify some points.

What is the business logic?

All applications have one or more goals, even really simple ones like >>hello world have one (which is printing hello world on the screen).

These goals reflect the needs of a real life problem within a certain scope. This scope can be identified as the business and all the rules that are applied in it are what is called the business logic.

When using an object oriented approach - specially when using a domain driven approach - is common to identify the entities involved in the business and map them in objects (the so called domain objects). The relation established between domain objects will also be part of the business logic.

When should Domain Driven Design (DDD) be used?

DDD is most effective when we have a large and complex domains. This happens because when DDD is well applied not only the model is well scalable but also self-contained. We'll see more about this below when the library management example starts.

Though, since there are no perfect solutions, DDD might not be the best solution to every software design problem. When having a simple domain - should be understood as few domain objects and not very complex business logic - using design patterns such as >>Transactional Script might be simpler and more effective.
I won't go into detail of Transaction Scripts since it's out of this post scope, but if anyone wants to discuss about them feel free to post comments.

How do we model a certain problem?

This is definitely the trickiest question. There is no formula to find the best model for a certain problem but there's a procedure on how to build a model.

  1. Identify involved entities;
  2. Define relations between those entities;
  3. Map business logic in the entities.
In my opinion, the best way to show how the modelling process works is by example. So, as has been said before I'll use the Library Management System(LMS) example which will be divided in four stages. This division is due to two main reasons, first because showing all the process in a constructive way is easier for the reader to follow and also because it shows that the concept of model design is scalable.

Stage 1: So you want to build a LMS

Problem: a system to manage library users and the books they reserve is needed. Should be kept in mind that a given user at any time can have a maximum of five reserves. If a book belongs to the adult section it can only be reserved by users who are over 18.

First we identify the entities related with the problem: there's the library user and the book (step 1). We have a relation between both, the act of an user reserve a book.
An user can reserve many books and a book can be reserved by many users in different periods of time (step 2).

lib-example1With this information we can already sketch a really simple model, as the one present at the right.

Now we collect the business logic in the problem. In this simple case, the business logic is the following:

  1. maximum of five reservations for user;
  2. users under 18 cannot reserve books from the adult section.
  3. underlying logic, a book can only be reserved at a certain moment if it's not already reserved by an user.
After the identification of the business logic, the decision about where to put has to be made. The 1st rule is related with the user, the other two rules are related with the book.
Here's a suggestion for the implementation (I hope there's no problemin joining two classes in the same code block)(step 3):

package net.pabrantes.example.library.model

public class LibraryUser { //other fields private List<Book> reservedBooks;

// more Code public addReservation(Book book) { if(reservedBook.size()<5 && book.canBeReservedBy(this)) { this.reservedBooks(book); book.isReserved(true); } } }

public class Book { // code here String section; boolean reserved;

public boolean isReserved() { return reserved; }

private boolean acceptedAge(Integer age) { return (!section.equals("adult")) ? true : age > 18; } public boolean canBeReservedBy(LibraryUser user) { if(!isReserved() && acceptedAge(user.getAge()); } // more code }

A book is reserved only if the user as less than five books (rule number 1) and if the book can be reserved by the user, notice that it's the Book that knows if can be reserved by a given user (implements rule 2 and 3).

Should be kept in mind that this kind of operations are called within a transactional context - for example, a service layer - that's why the variables aren't synchronized (a race condition on the isReserved can happen if the method isn't run as an atomic instruction).

In my opinion the worst problem in this solution is the lack of time tracking in the reservations, but since it's an example it's not that bad.

Stage 2: Registered and unregistered users

lib-example2 Problem: After a first implementation (stage 1) the concept of registered and unregistered library users is introduced. The main difference is that the registered user has no limit on book reservation.

Since both kind of library users are library users, a good way of modelling this part of the problem is by using >>inheritance. The new model can be seen in the schematic on the right. The super class will be abstract and contain all the shared behaviour.

On the Registered User there's only the need to override the addReservation method in order to remove the size verification.


Stage 3: The library also allows DVD reservation

Problem: The library also allows DVD reservation, but only for registered users.

We have a new entity, which is a DVD. Being the DVD and Book both items that the library allows reservation, it makes sense to create them as sub-classes of a new object called Library Item.

The Registered User has a relation - the reservation - with Library Item instead of Book, since it can reserve Books and DVDs. On the other hand the Unregistered User will keep the relation with Book only.

The model onces again changes and the result is:

lib-example3

Now the good thing is that the model applies the restrictions, there is no need for extra code. With such clean design there is no need for the use of the "ugly" instanceof operator in order to identified which type of library item an user is trying to reserve.

Stage 4: A bigger system, now only the users but also the employees

Imagine that the LMS domain is bigger than a simple the library users management with users, books, DVDs and reservations. It also has an administrative part, for example it has the concept of employee and all the accounting domain.

The subdomains could intersect each other if necessary, though, the employee subdomain and the reservation subdomain probably wouldn't! The only common point is that library users, and employees are both Person.

Leaving most of the accounting domain apart here is the new domain model: lib-example4

A question that might be asked is why aren't employee and library user sub-classes of person instead of having relations with it. Well the answer is simple, imagine that you have a Person that is employee and a library user at the same time. If you used inheritance you would have two different instances (one for employee and another one for library user) of the same person!

Conclusions

With this post I tried to give more detail about DDD, also an example of DDD was shown. With the previous example, it was not only showed how the process of domain modelling works, but also that DDD can simplify the domain grown and create self contained code.

Hope you've found this interesting.

Icon-Comment jpmsi, one year and 245 days ago. Icon-Permalink

Continuo com a minha opinião que falata aqui qualquer coisa. Isto de DDD é tudo muito bonito, mas falta perceber como é que isto dá para trabalhar num sistema de produção a sério.

Quando metes persistência ao barulho, é preciso abordar uma série de tópicos que de outra forma simplesmente não se vão resolver sozinhos, nomeadamente as relações entre objectos, como se obtém um ponto de entrada no sistema, concorrência, artefactos resultantes do modelo de persistência (chaves, timestamps) etc… Fico à espera do follow up :) And keep up the good reading!

Icon-Comment pabrantes, one year and 244 days ago. Icon-Permalink

É verdae que as questões que levantaste são bastante pertinentes, mas sabes bem - tal como eu - que quando tens um framework que te torna todo esse "problema" chamado persistência transparente (como é o caso do Fenix) o DDD torna-se bastante agradável.

Mas, sem dúvida alguma, seria interessante abordar os problemas relacionados com a persistência. Possivelmente um post por cada ponto que referiste, com uma introdução a padrões de desenho que possam resolver os assuntos e até links para frameworks já existentes que executem as tarefas em causa.

Obrigado pelo comentário!

Icon-Comment jpmsi, one year and 241 days ago. Icon-Permalink

O problema que levanto, nomeadamente no que diz respeito à persistência e controlo transaccional, é para mim um tópico quase impeditivo quando se trata de modelar negócio numa abordagem DDD. Isto porque é praticamente impossível contornar a arquitectura de persistência e manter o negócio completamente ignorante destes promenores. Desse ponto de vista, o trabalho do Prof. João Cachopo é realmente groundbreaking (sem descurar outras personalidades de relevo que também contribuem nesta área), no que toca a Apenas usando frameworks do género é que se consegue controlar todos os aspectos inerentes à utilização de DDD, tornando o código de negócio limpo e livre de fragmentos das arquitecturas que actuam por baixo.

Em todo o caso, como podes ver, não há código open-source para utilização livre, pelo que a malta de fora (leia-se fénix) fica à nora.

Icon-Comment pabrantes, one year and 236 days ago. Icon-Permalink

Não podias deixar de ter mais razão, é verdade que isto é tudo muito bonito de se falar, mas existem tópicos que deverão também ser abordados. Um destes tópicos tal como tu levantaste a questão foi a da persistência.

Devido a ser um assunto que por si - se me permites a expressão - tem pano para mangas esperava escrever um post só sobre ele. Não estás esquecido! Em breve :) Não será possívelmente o próximo, mas será dos próximos!

Sim é verdade que de momento não existe código open-source, se bem que penso que a intenção do Professor João Cachopo será realmente a de disponibilizar no final da sua Tese de PhD o código..

No entanto, existem outros frameworks que lidam com DDD e persistência talvez não de uma forma tão "alto nível" como o framework do Professor João Cachopo (que tal como disseste é realmente groundbreaking).
O exemplo que me ocorre é o >>Spring e o >>Hibernate, este tanto em java como .Net (já que tu agora estás no lado do .net).

Icon-Comment jpmsi, one year and 236 days ago. Icon-Permalink

Well, my experience with Spring is limited to say the least, but I do believe that in itself, the framework is used as an integration framework, meaning that it does no good in helping us with persistency. Hibernate, however, is a persistence framework with widely known good results. However, it does not completely hide code fragments derived from the use of a relational database backing the persistence itself. I'll be looking forward to reading your follow-up article!

Icon-Comment pabrantes, one year and 235 days ago. Icon-Permalink

Yes, Spring does no good in persistence, my suggestion about Spring was for the DDD approach. The hibernate was for the persistence, sorry if I didn't point that out. The idea is to combine both to have DDD + persistence.

For what I've read the Spring + Hibernate is in the "rails" track, simple domain using active record pattern. Still there are probably more complex solutions. I'll look into it!

Please login to www.pabrantes.net.
Who am I?
paulo-roca2My name is Paulo Abrantes AKA pabrantes and I'm a software developer. I'm currently employed at >>CIIST working as a Java developer in >>FenixEDU.

This blog is mostly about Java programming, domain driven design and snipsnap bliki developing. Everything written in this blog is my personal opinion and it may not reflect the opinions of my employer and co-workers.


Blog subscription
subscribe by rss subscribe by email

Links
>> Home
>> Paulo's Profile
>> Post History
>> Add to Technorati Favorites
>> Paulo's Photo Gallery
>> WishList
>> Posting without Login

Search Blog
Fellow Bloggers

Recent Posts

Java Programming: Bytecode Injection
Intermission: Sorry For Downtime
Software Developing: Studying The Bliki Domain Model
SnipSnap Developing: Trying to settle a roadmap
System Administration: Load Balancing with Apache
Blogging: Two years have passed
Software Developing: The SnipSnap Saga
Java Programming: Getting your code spicy with Groovy
Software Developing: Fluent Interfaces
Software Developing: Implementing a ShoutBox on SnipsSnip
Software Developing: SnipSnap, SnipIt and SnipSnip
Java Programming: Proxies and Access Control
Java Programming: Proxies and References
Java Programming: References' Package
YALM: Yet Another Layout Modification

For older posts, please refer to post-history for a complete Post History

Logged in Users: (0)
… and 7 Guests.
This is a modified version of snipsnap.org created by >>Paulo Abrantes