0

My application is using DDD with .NET Core and EF Core. I have some business rules that run within an entity that need to check dates against a cached list of company holiday dates. The company holidays are loaded from the db and cached by an application service that is configured with our DI container so it can be injected into our controllers, etc.

I cannot determine how, or if it's the right/best approach, to get the service injected into the entity so it can grab those dates when running business rules. I did find this answer that appears to demonstrate one way to do it, but I wanted to see if there were any additional options because that way has a bit of a code-smell to me upon first glance (adding a property to the DbContext to grab off the private constructor injected context).

Are there any other ways to accomplish something like this?

G_P
  • 2,100
  • 3
  • 16
  • 18

1 Answers1

1

ORM classes are very rarely your domain objects. If you can start with your domain and seamlessly map to an ORM without the need for infrastructure specific alterations or attributes then that is fine; else you need to split your domain objects from your ORM objects.

You should not inject any services or repositories into aggregates. Aggregates should focus on the command/transactional side of the solution and work with pre-loaded state and should avoid requesting additional state through any handed mechanisms. The state should be obtained and handed to the aggregate.

In your specific scenario I would suggest loading your BusinessCalendar and then hand it to your aggregate when performing some function, e.g.:

public class TheAggregate
{
    public bool AttemptRegistration(BusinessCalendar calendar)
    {
        if (!calendar.IsWorkingDay(DateTime.Now))
        {
            return false;
        }

        // ... registration code

        return true;
    }

    // or perhaps...

    public void Register(DateTime registrationDate, BusinessCalendar calendar)
    {
        if (!calendar.IsWorkingDay(registrationDate))
        {
            throw new InvalidOperationException();
        }

        // ... registration code
    }
}

Another take on this is to have your domain ignore this bit and place the burden on the calling code. In this way if you ask you domain to do something it will do so since, perhaps, a registration on a non-working day (in my trivial example) may be performed in some circumstances. In these cases the application layer is responsible for checking the calendar for "normal" registration or overriding the default behaviour in some circumstances. This is the same approach one would take for authorisation. The application layer is responsible for authorisation and the domain should not care about that. If you can call the domain code then you have been authorised to do so.

Eben Roux
  • 12,983
  • 2
  • 27
  • 48