2

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?

13
  • Does this answer your question? jeffreypalermo.com/2008/07/the-onion-architecture-part-2
    – Rik D
    Jul 21, 2020 at 20:20
  • I have my repository interfaces sitting right next to the implementations - in the infrastructure layer. They are coupled by a DI container. Not sure why it would be so advantageous to put the interfaces in the domain layer. To me, they don't represent a domain concern.
    – Hans
    Jul 21, 2020 at 20:34
  • @RikD not completely. In part 2, Jeffrey seems to take the exact approach that he had written as theory in part 1. He implements the interface in Domain Services and puts the implementation in Infrastructure, when that shouldn't be possible when I look at his Onion Architecture model. It seems to me that he sees the "Application Core" as 1 layer, instead of 3 separate layers, but then I don't see what the separation is for. Jul 21, 2020 at 21:22
  • @ZeHans Say if you want to switch from a MySQL DB to a Postgres DB, then you would have to modify your business logic as well, because your business logic depends on the interfaces that are defined with the MySQL implementation. For that reason it could be useful to place your interfaces in a layer more towards the center. I will join you in that I don't see how they solve a domain concern though. Jul 21, 2020 at 21:45
  • 1
    ’The fundamental rule is that all code can depend on layers more central’. Notice it’s plural. He doesn’t say code can only depend on the layer directly above, but all layers above (to the center). What’s more important is that no code can depend on lower layers.
    – Rik D
    Jul 21, 2020 at 22:27

1 Answer 1

2

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.

3
  • I really like this and I also like lots of the approaches Jason Taylor takes (although I also oppose some of his ideas like his lack of use of repositories). One issue I see is that UI can only depend on layers more towards the core, so it cannot reference the Infrastructure layer to configure the DI, because it is on the same level towards the core. I've seen some projects solve this by creating a separate "Bootstrapping" project that the UI will reference, but I think that is a little bit too dogmatic. Therefore I think the way you describe it in your answer is best. Jul 22, 2020 at 12:01
  • @octagon_octopus: It all depends on how you look at it. I consider that a top-level application (generally called the presentation layer) has an inherent responsibility of stitching the layers together, which inherently means that it needs to reference all the relevant layers. I don't see the need for having a separate bootstrapping project, since each top-level application technically needs its own bootstrapping project. But that's subjective. [..]
    – Flater
    Jul 22, 2020 at 12:06
  • @octagon_octopus [..] There are three possible approaches here: (1) Each top-level application stitches its components together (2) All top-level applications use the same bootstrapping project that defines how it's all stitched together (3) Each layer stitches itself onto its own dependencies, in a static method (e.g. IServiceCollection extension method) which the top-level application calls during startup (e.g. Startup.ConfigureServices(). I (and Jason Taylor) prefer option 3. Personally, I consider option 2 irrelevant as 1 or 3 are always a better fit. But again, that's IMHO.
    – Flater
    Jul 22, 2020 at 12:07

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.