dinsdag 14 februari 2006

Domain Driven Design / Entity (Yourdon-Chen) approach

The last few months, I've been reading a lot in the book Domain Driven Design by Eric Evans.
The book preaches really useable things such as 'the separation of concerns', and 'expressive domain models'. These are practices where I try to strive to, and the more I read in the book, the more this 'Domain Driven paradigm' appears to me as 'the Holy Grail' in software developmment.

However, when you want to turn theory into practice, you sometimes run into problems...

For instance, suppose you have a simple domain model for some shop. In this domain model, you'd have a Customer entity, and this Customer has Orders. An Order consists of one ore more OrderLines.
Suppose we should treat a Customer who ordered for more then 1000€ in the last 6 months as a 'Gold Customer'.

In a Domain Driven Design, it would feel natural to add a member to the Customer which tests this condition, and it could look like this:
public class Customer
{
...
public bool IsGoldCustomer()
{
decimal total = 0;

foreach( Order o in _orders )
{
if( o.OrderDate >= sixMonthsAgo )
{
total += o.TotalOrderValue;
}
}

return ( total > 1000 );
}
}


As you can imagine, this will not be very performant. First of all, this code requires us to traverse over the entire OrderCollection of that customer.
It could also be that the TotalOrderValue property is also a calculated value.
This means that, for every Order who's orderdate falls within the last 6 months, we'll have to retrieve the OrderLines (suppose that the OrderLines are lazy-loaded) in order to be able to determine the TotalOrderValue of that Order.
As you can see, this means that we'll have to do a lot of query-ing and we also have to retrieve a lot of records out of the database and load them into memory... For just such a simple check...
This is not really a best practice.

Luckely, our database is able to determine the amount of placed orders in the last 6 months for a specific customer in a much faster way, and without the need of loading a lot of objects into memory.
By using a Specification pattern, we can make use of the power that our database offers, and still have the logic encapsulated into the domain:

public class CustomerGoldStatusSpecification : CustomerSpecification
{
public bool IsSatisfied( Customer c )
{
decimal total =
customerRepository.
GetTotalOrderValueForCustomerInPeriod(c.Id, sixMonthsAgo, today);

return (total > 1000);
}
}


In this way, the logic is still encapsulated in the domain model, since the CustomerGoldSpecification class is a part of the domain, and, we also make use of the power of our database; the specification uses a method on the Customer Repository which queries the database for the value that we want.

Nice. :)

However....

If we take this logic out of our 'entity' classes, then what difference does this 'Domain Driven' approach make with the Yourdon/Chen entity approach ?
If we take the 'complex' logic and behaviour out of our domain classes, what else is left but some dumb entities that are nothing more then a data-holder, while all our logic is in specifications / managers ?

One could ofcourse say that the Specification class can be made internal, and the Customer should use this specification class like this:
public class Customer
{
public bool IsGoldCustomer()
{
CustomerGoldStatusSpecification spec =
new CustomerGoldStatusSpecification();

return spec.IsSatisfied (this);
}
}

(You can offcourse also directly access the repository from this Customer class)

Am I exagerating about this ? Is the situation I've described here, and the solution a good solution ? Is there still a difference between this approach and the Entity/Yourdon approach ?
I'd like to hear your comments and opinions.

2 opmerkingen:

Naren zei

Yeah.. that's about all the difference between the two..

JupiterMoonBeam zei

>> If we take the 'complex' logic and behaviour out of our domain classes, what else is left but some dumb entities that are nothing more then a data-holder, while all our logic is in specifications / managers ?

No this isn't what you are supposed to do. Dumb entities is moving towards an anemic model which is the exact opposite of what DDD is about.

The idea of specifications isn't to take complex logic out of your domain classes but to allow business rules to become selfcontained. This way your class isn't cluttered with noise and you can reuse specifications across your application preserving them in the domain removing the need to spread the logic across the various layers.

Avoid the tempation of just removing every piece of 'complex' code in out of your entities and placing them into a seperate class.

Also some may question whether your entities should be accessing repositories directly (this gets a bit religious though!).