5

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?

8
  • Whats stopping you from putting the interface in domain and implementation somewhere else? Oct 25, 2019 at 11:28
  • @EsbenSkovPedersen Nothing is, that's what I've done now for the time being. My question is whether the interface should be in the domain layer, as it wasn't so much created to directly model part of the business logic as it was to improve the testability/maintainability of another service within the infrastructure layer.
    – Tagc
    Oct 25, 2019 at 11:31
  • What is the dependency direction between the Domain and Infrastructure layers? If you have opted to strictly follow clean architecture, if the Domain Layer is at the center, and your BranchStateMonitor is part of it, if you put the IOrderChangeNotifier in the infrastructure layer, then you are going to make Domain depend on Infrastructure. If it stays in the Domain layer, then think of it as being a part of the abstraction represented by BranchStateMonitor (i.e., it's an a required interface of BranchStateMonitor) Oct 25, 2019 at 13:05
  • In other words, it's dependency inversion, but on a higher level then just classes and interfaces --> domain and infrastructure are two components, and there are some classes and related interfaces on the boundary that represent an abstraction of some service provided by the infrastructure layer and beyond. Oct 25, 2019 at 13:08
  • @FilipMilovanović The infrastructure depends on the domain layer, but the domain knows nothing about the infrastructure layer. IBranchStateMonitor is defined in the domain layer but BranchStateMonitor exists in the infrastructure layer. Putting IOrderChangeNotifier in the infrastructure would not require the domain layer to depend on the infrastructure layer, because IOrderChangeNotifier is an abstraction that's only referenced within the infrastructure layer (specifically, by BranchStateMonitor).
    – Tagc
    Oct 25, 2019 at 13:12

3 Answers 3

3

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.

2
  • "which could mean that your bespoke application code depends on a stable general purpose library" - I wouldn't take it so far, but limit it to the code you own / have control over. Essentially, I think "stability" is used in a different sense here - this idea should be appied to the code that models and implements the domain and related application logic, because as the code evolves, you can get a sense of what ideas are more stable w/ respect to change requests and the dynamics within the domain itself. Otoh, you may want to avoid the risk of depending on a 3rd party lib, even if it's stable. Oct 25, 2019 at 13:16
  • The article on SAGE was interesting, thanks. One excerpt from it: "Put general-purpose things in general-purpose places, and put special-purpose things in special-purpose places." IOrderChangeNotifier feels rather special-purpose in that it's really only meant to improve the maintainability and testability of an implementation class within the infrastructure. The infrastructure layer is also less general (more special) than the application core layer. Based on this, it feels like IOrderChangeNotifier belongs more in the infrastructure layer.
    – Tagc
    Oct 25, 2019 at 15:58
0

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.

0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.