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, 2 years and 340 days ago. Viewed 3,093 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.

6 comments (by pabrantes, jpmsi) | post comment
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

Blog: Almost an year since last post
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

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

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