0

Usually the domain logic should be contained in the domain entity, rather than being put in specialised external classes called from the entity.

This is good to prevent that somebody comes later, changes the class with the domain logic and affects, without knowing it, the domain entity.

Example

Human {
    private birth_date;

    public calculateZodiac();
    public calculateBadLuck();
}

The methods rely on the birth_date and encapsulate the entity domain logic.

But what if..

in a specific context I need to save resources and I can't afford to create an instance of the domain entity but, just knowing the birth date, I want to still be able to calculate the zodiac and the bad luck?

Solutions

Domain logic in external class

I could have the logic in a Zodiac class, that gets all the needed data (in this case: a date) and returns my calculations. This is also good for separating concerns, improve readability (rather than having a huge domain entity containing the complex code for all concerns regarding Humans).

It would also be nice for being able to reuse that logic in different domain entities (e.g. Animal).

So calculateZodiac() would instantiate a new Zodiac, calculate() stuff and return it.

But, as said above, this can be changed by somebody without noticing the direct side effects on the domain entity.

And it goes in the direction of an anemic domain model, that is considered an anti-pattern by Martin Fowler.

Composition

When instantiating the Human domain entity, always provide the Zodiac implementation to its constructor.

Same risk as before, but at least the dependency is explicit.

Also, after providing the Zodiac, do the same for the classes PsychopathProfile, ExpectationOfLife, ProbabilityToBeBulliedBasedOnUnfortunateName, SportivePotential, etc..

Too much stuff in the constructor, each time I instantiate. Not ideal.. 🙄

Just duplicate the code & keep integrity with unit-testing

Accept that when not working directly on the domain entity then the concern is just a different one, even if the logic should be the same.

So just take the logic from the domain entity, put it in a ZodiacFromBirthDate class and use it without a domain entity.

And to ensure that nobody comes later and breaks something, just add some tests that ensure that the Human.calculateZodiac() method always returns the same values as ZodiacFromBirthDate.calculate() method.

Others..

Can you come up with other solutions?

How to ensure that the domain logic is not accidentally changed, while still allowing to reuse the logic also without having a domain entity available?

1

You might need a BirthDate class or struct that contains all the logic you are talking about.

With just a little brain storming, there are a number of things you can derive from a "birth date" that would be perfectly fine to include in a BirthDate class:

  • Calculating the Zodiac sign
  • Calculating "bad luck"
  • Age (yup, RightNow - BirthDate = age)

If you have a language that supports operator overloading, you can also overload mathematical and logic operators so you can do things like:

var birthDate = new BirthDate(1982, 12, 14);
var theYear2000 = DateTime.Parse("1/1/2000");
var isBornAfterYear2000 = birthDate > theYear2000;

Console.WriteLine(isBornAfterYear2000); // Prints "False"

Usually when you find logic and data spanning multiple types in your application, it just means you are missing a type that encapsulates both data and logic, and then those other classes need to use this new missing class.

You can still have a Human.calculateZodiac() method, which then delegates that operation to its internal BirthDate object to return the correct value.

0

Generally, the larger a project gets, the dumber I like my models to be.

I know following DDD you should have domain models that are cohesive enough to do domain model specific calculations... but the second you have a calculation that applies outside of your domain model, to me it really cleans the code and provides more benefits to provide that calculation somewhere else (service class, utility, etc).

My suggestion would be:

birthDateCalculatorService.calculateZodiacSign(myHuman.getBirthDate());
birthDateCalculatorService.hasBadLuck(myDog.getBirthDate());
  • 1
    Your approach is quite literally the antithesis of DDD. Passing blobs of data around to functions fundamentally procedural in nature, and therefore much better understood in functional terms. The two examples you provide would be better implemented as standalone functions. E.g. calculateZodiac(date). – king-side-slide Dec 15 '18 at 18:42

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.