Should an interface be defined in the infrastructure layer if it's only used by an implementing class in that layer?

softwareengineering.stackexchange https://softwareengineering.stackexchange.com/questions/400138

Pergunta

Overview

If an application is being developed following a clean architecture / DDD approach, my understanding is that the application core or domain layer should contain classes and interfaces that directly model the business logic and represent concepts and events that the end user of the application understands and has a stake in.

In my case, I'm trying to implement a service but I want this implementation to rely on other services that are abstracted behind an interface (for easy swappability/testability). The issue is that the abstraction is only tangentially related to the business logic, so I'm not sure whether to create the interface (and associated events it publishes) in the domain layer or the infrastructure layer.

Background

I'm trying to develop an ASP.NET Core application using a Clean Architecture approach, with separate projects for the API, application core/domain logic and infrastructure.

The gist of this project is to serve as a backend for an SPA that presents an "order dashboard" to sales managers, allowing them to track the progress of orders belonging to different product consultants within the company branch they're in charge of.

In my domain layer, I've defined events, entities and services that I'm fairly confident belong there, because they're directly involved in modelling the business logic and represent things that the end user (the sales managers) would understand and have a stake in:

However, the way I plan to implement IBranchStateMonitor is to make it subscribe to order event streams within EventStore and then query the database for that particular order (through an EF Core DbContext) when events for it are published (in order to determine if the state of the order has changed in a manner relevant for this application).

I want to decouple the logic for querying the current state of orders from the logic used to detect when the states of the orders may have changed. For example, instead of subscribing to event streams in a cloud-based service, I might prefer a simpler alternative that involves just prompting BranchStateMonitor to refetch the states of the orders from the database at regular intervals. Based on that, I'd want a class structure like this:

Here's the question: should I define IOrderChangeNotifier and OrderChanged within the domain layer or within the infrastructure layer?

Foi útil?

Solução

The most effective designs I have seen isolate the domain model in the local process; information from "somewhere else" is passed to the domain model as in memory values. (In effect, the domain model is "just" an in memory finite state machine).

Thus, the protocol for fetching those values across a process boundary belongs somewhere else -- for instance, in the application. All of the interfaces, abstractions, etc that you require for describing and executing those protocols lives somewhere else.

Dependency Inversion Principle suggests that the definition of the abstraction lives with the client code, which in this case is the application.

SAGE suggests that what you really want is for the less stable parts to depend on the more stable parts -- which could mean that your bespoke application code depends on a stable general purpose library.

But regardless, outside of the domain model, which lives entirely within the functional core.

See also Boundaries, by Gary Bernhardt.

Outras dicas

From the initial look IOrderChangeNotifier and OrderChanged are clearly part of the domain.

If I understood the responsibility of your your application correctly, it is present the orders and it's state to the sales managers. In this case, the IOrderChangeNotifier and OrderChanged is definitely part of your domain.

You could implement IOrderChangeNotifier and OrderChanged in the domain layer. The class BranchStateMonitor also would be in the domain layer, as this contains the branch monitoring logic that is core business. Now class BranchStateMonitor could reference IOrderChangeNotifier.

The implementations of IOderChangeNotifier - EventStoreOrderSubscriber and PeriodicOrderChangeNotifier will be in the infrastructure layer.

Now the interface is defined in the domain layer, it is used (referenced) in the domain layer and it is implemented by class in the infrastructure layer - All of this looks perfect to me.

For the sake of brevity, I'm going to refer to it as the "internal interface" (= only referenced inside its own assembly). I'm not talking about the internal access modifier itself, though (spoiler alert!) you may still end up making this "internal interface" internal in the end.

This depends on who decides which implementation of the child interface should be used?

If your library decides this by itself, then there's no reason for this child interface (or its implementations) to ever be exposed publically, since by definition no external consumer will be allowed to weigh in on the decision making process.

However, if the consumer (e.g. web service) gets to choose the child implementation, then the child interface (and its implementations) must be known publically in order for the decision to be made.

In short, inversion of control requires you to evaluate who is supposed to have the control. This tells you whether you should expose your interface or not.


That being said, there are architectures where this distinction is moot. I've worked in several projects where the interfaces were placed in a separate project from their implementations - at which point you cannot avoid making all interfaces public.

This is an architectural decision that is made on a higher level and is usually set in stone by the time development has progressed to where your question arises.

Licenciado em: CC-BY-SA com atribuição
scroll top