We use DDD in our project in financial domain.
I have a value object:
final class VatRate extends DecimalNumber {
}
abstract class DecimalNumber
{
public static function fromString(string $number): static
{
return new static(\Brick\Math\BigDecimal:of($number));
}
}
I need to create zero vat rate in domain aggregate.
Both aggregate and VatRate
exists in domain layer.
This is the way I am doing it:
$zeroVat = VatRate::fromString('0.0');
I was told it is not right. I should doing it this way:
final class VatRate extends DecimalNumber {
public static function zero(): self
{
return self::fromString('0.0');
}
}
$zeroVat = VatRate::zero();
What is the point to encapsulate domain logic in this case?
The only response I had is that it is more "Domain" way of doing things.
When I need to create VAT rate 0.20 then should I similarly create static method VatRate::zeroPointTwenty()
?
What are the pros and cons of both solutions?
What is the point to encapsulate domain logic in this case?
"Information hiding" -- see Parnas 1971.
Basic idea, by creating a seam between the code that knows how to use a zero
and the code that knows how to create a zero
, we reduce the amount of code directly impacted if we need to change the underlying data structure or initialization routine.
In addition, by introducing a seam, you create the opportunity to introduce an intention revealing name.
If your development environment has a decent indexer, you'll easily be able to find usages of zero
without getting your results cluttered with a bunch of other calls to fromString
with different arguments.
Another way of thinking about this: strings are not "of the financial domain", they are the general purpose data structure that this implementation just happens to use. Therefore, they don't "belong" in the domain specific language we are creating to describe to programmers how this code does something useful to the business.
There are lots of ways we can tell the computer to do the right thing. The goal is to use that freedom to better communicate with the next programmer to come along.
What are the cons? It's trade offs all the way down, right?
The main downsides are this: that it's another piece of code to keep track of. You've got to find a good place to put it, and make sure that everybody who needs it can find it. If you've got a bunch of "unit" test zealots around, then maybe they are going to insist on a bunch of testing ceremony to go with this new one-line-function.
It's not "free" - especially if it isn't your current habit. It's extra work we do today, while all of the context is fresh in our minds, so that the people who have to work in this code later have an easier time of it.