TransWikia.com

How should infrastructure be able to reference repository interfaces in onion architecture?

Software Engineering Asked by octagon_octopus on November 11, 2021

In Jeffrey Palermo’s article about Onion Architecture, he claims the following:

The first layer around the Domain Model is typically where we would find interfaces that provide object saving and retrieving behavior, called repository interfaces. The object saving behavior is not in the application core, however, because it typically involves a database. Only the interface is in the application core.

enter image description here

The first layer around the Domain Model is the Domain Services layer, so this would mean that repository interfaces should be defined there.

However, later on Jeffrey also says this:

Out on the edges we see UI, Infrastructure, and Tests. The outer layer is reserved for things that change often. These things should be intentionally isolated from the application core. Out on the edge, we would find a class that implements a repository interface. This class is coupled to a particular method of data access, and that is why it resides outside the application core. This class implements the repository interface and is thereby coupled to it.

It is unclear to me how this can work. Jeffrey claims that the implementation of repository interfaces should remain in the outer layers (I think the implication here is the Infrastructure layer), but the interfaces reside in the Domain Services layer. How is the Infrastructure layer supposed to get to these interfaces? From the looks of his Onion Architecture model, the flow of references look like this:

Infrastructure -> Application Services -> Domain Services

In short, how is Infrastructure supposed to provide implementations of interfaces it cannot reference? Am I just misunderstanding this and should the interfaces reside in the Application Services?

One Answer

Confusion in the blog post

How is the Infrastructure layer supposed to get to these interfaces? From the looks of his Onion Architecture model, the flow of references look like this:

Infrastructure -> Application Services -> Domain Services

You are correct that there seems to be some confusion on the specific layers being used in the blog post itself.

The first layer around the Domain Model is typically where we would find interfaces that provide object saving and retrieving behavior, called repository interfaces. The object saving behavior is not in the application core, however, because it typically involves a database. Only the interface is in the application core. Out on the edges we see UI, Infrastructure, and Tests.

These two statements imply that "the first layer around the Domain Model" is "the application core", but on the graph they are different. "The first layer around the Domain Model" is Domain Services, and "the application core" is Application Services. That's either a contradiction or a mistake in the explanation.

If you ignore the image and only read the explanation, the blog post seems to lump the Application Services and Domain Services (from the circular graph image) together as "the application core".

The added graph just doesn't quite match, this may have been a simple oversight when adding the reference image.

Note that this is how I also structure my layers in my solutions, where the Application Services and Domain Services from the image are just one "Application" layer. I agree with that approach as described by the explanation, but the added graph just doesn't quite match.

For the purpose of this answer, I'm going to follow the explanation, not the image; and lump the "application services" and "domain services" layers from the image together and call them the "Application" layer.

A more correct representation would be

Test -> Application <- Infrastructure
            ^
            |
            UI

Note: we can argue about the direction of the arrow. In the above case, A -> B means that A has a project dependency on B. I usually flip those arrows around because it matches my mental picture of a dependency graph.

Note that this flow mirrors the general intention of the picture with the circles you posted: the application layer sits in the middle (wrapped around the domain), and the other layers surround it.

Or using my favorite resource on the topic (all credit goes to Jason Taylor):

enter image description here

Note that this graph separates Persistence from Infrastructure, but that's an irrelevant distinction for the current question. You can lump these together if it makes more sense to you.
In the Clean Architecture solution template that Jason Taylor authored (much more recently than these slides), Persistence has also been lumped back into the Infrastructure layer


Using inverted dependencies

First, realize that when your applications runs, all layers are available at the same time. This is essential to understanding why dependencies can be inverted.

During compile time, your Application layer doesn't need to know exactly who implements your IRepository interface. It only needs to know that the IRepository interface itself exists. It blindly trusts that (at runtime) it will receive a valid implementation of that interface.

During runtime, the top-level application registers your concrete Repository class (in the Infrastructure layer) as that IRepository interface (from your Application layer). Something along the lines of:

services.AddScoped<Application.IRepository, Infrastructure.Repository>();

Note: whether the top-level application registers this itself, or delegates that responsibility back to the Infrastructure layer, is not relevant right now. I generally prefer the latter but the principle remains the same.

Your top-level application can do that because it inherently has access to all of the libraries that it uses, in this case your Application and Infrastructure projects.

This is what's called an "inverted" dependency.

A normal dependency would be one where if A has B as a dependency, then A's project would have a project dependency on B's project. In this example, that would mean that Application depends on Infrastructure, and Infrastructure would be providing both the IRepository interface and the Repository : IRepository class.

But in an inverted dependency, if A has B as a dependency, then B's project has a project dependency on A's project. That's the other way around, which is why it's called an inverted dependency. To make this possible, it requires A's project to define the interface and B's project to implement the interface.

In your case, that means that Infrastructure depends on Application. Application provides (and works with) an IRepository interface, and Infrastructure will implement a concrete Repository : IRepository.

Answered by Flater on November 11, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP