DateTime.Now in Domain Layer of DDD
Asked Answered
D

1

6

Recently I faced with the following invariants in my domain Model:

  1. An Offer treated as Expired if ExpiryAt (DateTimeOffset) < DateTimeOffset.Now.

  2. A Director of the Company cannot be younger than 18 years old

  3. When Document is downloaded we should set DownloadedAt field with DateTimeOffset.Now

In Application Layer to keep purity and for better testing we usually isolate System.DateTime with IDateTime interface which allow to mock Now in UnitTests.

But all these 3 scenarios belong to Domain Layer and not to Application Layer. We should not Inject external interfaces into DomainModel to keep it pure. But from other side it might be bad to use DateTime.Now or DateTimeOffset.Now directly in DomainLayer since this adds a dependency to system clock and make it harder to test sometimes since DateTime.Now will never return the same result.

So the question is - how do you deal with this dilemma?

Options I see:

  1. Provide now as parameter to Domain Entity methods. This is viable option and simplify testing though makes code more verbose and sometimes even stupid.

  2. Just use DateTime.Now in domain layer. I already mentioned cons of this approach.

Anything else you might suggest from your experience?

Donniedonnish answered 18/2, 2022 at 19:55 Comment(0)
U
6

From the different options accessing the static DateTime.Now() functionality is obviously the most disadvantageous. It both does not allow for testing and also hides the domain models dependency to some non-deterministic infrastructure inside the implementation details.

The option to inject some interface to a service that can be viewed is a little better because it makes the dependency explicit and also allows for unit testing by stubbing the non-deterministic output to return some deterministic value of your choice.

But still, at runtime your domain model needs to access some infrastructure dependency. This might be a reasonable compromise in some cases, but if possible I would try to avoid that to keep the domain model pure.

If you look at the current date time in your case from a different angle it becomes more obvious that it is actually nothing else like a normal input parameter. You could see it as something like a reference date time instead of the current date time.

Referring to your first example - checking if an offer has expired - from the domain model's point-of-view it needs to check if the offer has expired at some given point in time. This given point in time just happens to be current date time in one of the use cases where the domain logic is exercised.

So bottom line, I recommend to inject the value of the (current) date time rather than an interface to some functionality in such cases. It makes explicit what data is really needed in addition to the data the domain encapsulates on its own and requires for performing the business logic.

Also, it makes more explicit what the client code (e.g. the use case or application service) wants to tell or ask the domain model. For instance, check if the offer has expired as of now or if needed, tell me if the offer was already expired at a given point in time or even if it will be expired at an important point in time.

As further reading I recommend this great article from Vladimir Khorikov where he elaborates more on that topic.

Undenominational answered 19/2, 2022 at 8:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.