7

I have a business requirement that any account viewed needs to be logged. I'm trying to figure out where this requirement should be in my code. This is a query type event and doesn't require going through the entity itself like most of my domain events.

Should I encapsulate all of my repositories into domain services that my application service will call?

public class AccountService
{
    private readonly IAccountRepository repository;
    private readonly IDomainEventDispatcher dispatcher;

    public AccountService(IAccountRepository repository, IDomainEventDispatcher dispatcher)
    {
        this.repository = repository;
        this.dispatcher = dispatcher;
    }

    public Account ViewAccount(Guid Id)
    {
        var account = repository.GetById(Id);
        dispatcher.Raise(new AccountViewedEvent(account));
        return account;
    }
}

Is there another way to handle this?

It seems like a lot of layers to have an application service call a domain service that calls a repository.

Todd Skelton
  • 191
  • 6
  • 1
    Logging is a cross-cutting concern. Have you considered standing up a `static` logger object, using [Tracing and Instrumentation](https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/tracing-and-instrumenting-applications) in the .NET Framework, or otherwise implementing a typical `ILogger` implementation? – Robert Harvey Mar 26 '18 at 22:13

1 Answers1

3

Domain Services don't typically decide to raise events on their own -- that sort of logic is normally reserved for the domain model itself. For example, if we change the example slightly

public Account ViewAccount(Guid Id)
{
    var account = repository.GetById(Id);
    if (null != account) {
        dispatcher.Raise(new AccountViewedEvent(account));
    }
    return account;
}

you can get a sense for the fact that it's a model's decision that an event is appropriate; the dispatcher is just implementing the capability.

It seems like a lot of layers to have an application service call a domain service that calls a repository.

Yeah, that sounds too elaborate.

The right way to go really depends on the motivation for the requirement. What is the business value of the logging views? Do you have a model whose correct behavior depends on previously logged values?

If you've got a "write only" log, then domain events are probably overkill; as is the repository. Create an interface for the logging capability at the correct abstraction level, and just wire it directly into your logging framework.

On the other hand, if you've got requirements for doing interesting things with the AccountViewedEvents after they have been logged, then you've probably got an implicit model that should be dragged into the light, and start asking questions like "is this part of the same domain that accounts live in, or is it something else?"

I don't know about doing interesting things with the events, but it's to satisfy HIPAA requirements and extremely important. This is why I wanted to make it part of the domain

That's definitely something you want to make explicit

public Account ViewAccount(Guid Id)
{
    var account = repository.GetById(Id);
    if (null != account) {
        hipaaCompliance.recordForAudit(new AccountViewedEvent(account));
    }
    return account;
}

You may even want to design your API such that you can only access the Account via the HIPAA compliance module.

VoiceOfUnreason
  • 32,131
  • 2
  • 42
  • 79
  • I don't know about doing interesting things with the events, but it's to satisfy HIPAA requirements and extremely important. This is why I wanted to make it part of the domain. – Todd Skelton Mar 27 '18 at 15:37