Steven Brookes
Steven Brookes

Reputation: 894

DDD time-based invariant

I am currently trying to implement DDD into an existing system, and have a bit of a head-scratcher with some of an invariant of one of my models

Working on a payroll system, some of the invariants change at the beginning of a tax year, but the system still needs to support the current tax year. So for example, if the minimum full-time working age is 16 at the moment but next tax year it changes to 15, is that something I could handle with DDD?

Currently there is a repository of reference data which has start date/expiry date which can be read from using the current date to determine the correct record to use at a given date, but from what I understand using repositories in the model is a bit no no.

Am I missing an obvious point with DDD (e.g. because it requires a repository, its not an invariant of the entity), or is there a way I can work this? (e.g. are they actually separate aggregates when such a change comes into force?)

Upvotes: 1

Views: 960

Answers (3)

Batavia
Batavia

Reputation: 2497

This is one of the reasons why you should factories. For example from your case it would make very much sense to model an EmploymentContract

Now you would get code like this

public EmploymentContractFactory {

    public EmploymentContract(DateTime starttime, Person p) {
          if (starttime.Year == 2016 && p.Age < 16) {
             throw new TooYoungException();
          }
          else if (p.Age < 15) {
             throw new TooYoungException();
          }
          return new EmploymentContract(starttime, p);
    }
}

The idea of DDD is that your entities/factories are smart enough to know when they are valid and what can be created.

It depends a bit on what exactly you are moddeling/the complexity of your domain. For example if you domain deals a lot with rules related to contract you might even a "ContractRule" which can evaludate some law against your model contract. In that case you could ask the Lawbook (aggregate root) to return all applicable laws/rules at a certain date and use those in your factory to see if you are allowed to create that particular contract.

Upvotes: 0

Constantin Galbenu
Constantin Galbenu

Reputation: 17683

If your aggregate need that time-base information then you must provide it, no matter where it is persisted. On the other hand, the aggregate should have the minimum dependencies to external services, you should keep them clean and pure; so the aggregate should not do any IO, not even an abstract one (by using an interface owned by the domain and implemented in the infrastructure).

That being said I would query that repository of reference data before the call to the aggregate command method and I would pass that information as a value object parameter. So, I will not inject any service in the aggregate, not even as a parameter to a command method.

Think it otherwise: does the aggregate care where that information is store or even that it is stored somewhere? No, it only need the information and Application services are best at reaching repositories and loading the required data from there.

Upvotes: 1

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57279

Currently there is a repository of reference data which has start date/expiry date which can be read from using the current date to determine the correct record to use at a given date, but from what I understand using repositories in the model is a bit no no.

The real concern with using repositories is trying to access the "current" state of something than can be changing concurrently with the work you are doing.

There's nothing fundamentally wrong with accessing immutable state from the model; in discussions, this is usually represented as a DomainService (aka: a "pure function") that gets passed to the model, rather than as a Repository.

In your situation, the tax policy isn't controlled by your model; it's something imposed on you from the outside world; the best your model can do is work with the most recent version of the policy history anyway.

In short, you are going to model time, and you are going to have a domain service that, given a time, returns an immutable representation (state) of the tax policy appropriate for that time (as best you know it), and the model is going to explicitly document which tax policy was satisifed, and not merely what key was used to look up that policy.

Upvotes: 4

Related Questions