Reputation: 11
I'm making an app, trying to stick to DDD concepts and practices. I have a rich domain model, with entities, value objects, business rules encapsulated within them, etc.
Entities don't really have too many fields and are relatively small when it comes to contained data, but the problem is, some of them got really big (as in: classes representing them) because of lots of possible interactions with those entities.
You know,
void DoThis();
int GetThat();
bool CheckForThis();
every interaction with some piece of my domain is represented by a public or internal method, each of those methods containing some business rules.
I tried to extract that logic into internal classes I call policies, so instead of
public int CalculateAmountOfCoolThingy()
{
int result = 0;
/*
Super duper complicated calculations here with 100 lines of code.
*/
return result;
}
in my entity, I have:
internal sealed class CoolThingyCalculationPolicy
{
public int CalculateAmountOfCoolThingy(MyEntity entity)
{
int result = 0;
/*
Super duper complicated calculations here with 100 lines of code.
*/
return result;
}
}
and in my entity:
public int CalculateAmountOfCoolThingy()
{
CoolThingyCalculationPolicy policy = new CoolThingyCalculationPolicy();
int result = policy.CalculateAmountOfCoolThingy(this);
return result;
}
Is this the way? Is this a good practice? Because it doesn't feel entirely right. Maybe there's some better approach?
Upvotes: 0
Views: 724
Reputation: 57214
I tried to extract that logic into internal classes I call policies.... Is this the way? Is this a good practice?
Probably not; the samples you provide suggest that you are designing an Anemic Domain Model. You might also want to review Tell, Don't Ask.
My view: big vs small is really an evaluation of the size of the data model controlled by the entity, rather than by the number of different methods you have that act upon that data model.
For example, if you have a single integer field, and 100 methods that need to lock that field (typically: while changing it), then you are going to end up with a single entity with 100 methods.
On the other hand, if you have two fields, and 100 methods that need to lock either field A or field B, but no method needs to lock both fields, then your entity is "too big", and you should consider using two entities instead.
The analysis metaphor that I use is that of a connected graph - draw an edge between each pair of values that need to be locked at the same time, then separate the graphs so that you can lock each without locking any of the others.
It's somewhat common to analyze a concept like "Customer" and discover that what you have are many disconnected graphs that all share the same lock. Those are entities that you should consider breaking up.
Upvotes: 1
Reputation: 711
Yes, you can encapsulate those rules in another class named domain services. But remember that you must inject the service into your domain model.
There are three attributes of a design that you should consider. The domain purity, completeness, and performance. You can't have it all at the same time.
In this article, Vladimir Khorikov talks about these three and I suggest you read it thoroughly. In the conclusion, he suggests that domain purity is more important than completeness (you can read about the reasons in the article). So I suggest that:
Another thing to consider is that moving logic to domain services is not recommended unless you have a good reason for it. Fat domain services mean thin domain models which results in anemic domain models. To manage your domain models better, consider separating them into smaller building blocks like entities and value objects. Using domain services to make smaller models feels like cheating. Because the responsibility should be on the domain model and you force it into another object. It's not a good reason to create domain services unless you want to reuse some logic or calculations.
Upvotes: 0