AlexQL
AlexQL

Reputation: 23

Clean Architecture and circular reference problems between projects

I'm having trouble implementing a simple test application using Clean Architecture.

Describing the test application, it is a simulation of a sales system, with a single use case and divided into 3 packages (DLL, made in C#):

  1. Sales.Business
  2. Sales.Adapters
  3. Sales.Infrastructure

In the Sales.Business package (yellow and pink circles), we will have:

In the Sales.Adapters package (green circle), we will have:

In the Sales.Infrastructure package (blue circle), we will have:

enter image description here

Here is where the problems start.

The "StartOrder" use case needs to access the OrderRepository to save the started order.

Since this would be a violation of the Clean Architecture dependency rules (a high-level element accessing a lower-level element), this access is done via dependency inversion using the IPedidoRepository interface.

So we have the following reference:

StartOrderUseCase --> IPedidoRepository

The IPedidoRepository interface is something like this:

namespace Sales.Adapters;

public interface IOrderRepository
{
...
int SaveOrder(Order order);
...
}

Note that the interface references the domain entity "Order" (and this is acceptable from the point of view of dependency rules), but this creates a circular reference problem in the project because we have:

Sales.Business references Sales.Adapters

And we have:

Sales.Adapters references Sales.Business

If I replace the Order class in this interface with an OrderDTO like this...

namespace Sales.Adapters;

public interface IOrderRepository
{
...
int SaveOrder(OrderDTO order);
...
}

... the problem is the same because the DTO will need to be used both in the Sales.Adapters package and in the Sales.Business package. No matter where I put this OrderDTO, there is always a circular reference.

Does anyone know how to solve this problem?

Upvotes: 1

Views: 942

Answers (1)

eocron
eocron

Reputation: 7546

The problems with any C# "architecture" comes when you need to combine classes/interfaces, so common problem looks like this:

C 
| \
A  B

Lets imagine you want your code to change as least as possible, so if library created - you want to develop it once and just apply minor bugfixes. So in our case A and B now locked for modification. The only way for us to create new type C is by creating separate library which will hold C.

All in all this "problem" in production is solved by:

  1. For integration classes (A, B in our example) with other systems (SQL, RabbitMQ, Kafka, elastic, logs, DI, common classes, etc, sometimes it is called DAL or Data Access Layer library but for me it is just wrapper library) - create a library, put your adapters without any business logic there, put some request/response DTOs there too, no need to separate them, you are abstracting out your dependency aka creating your own protocol to talk with those systems in case they will change later.
  2. For aggegate classes (C in our example) create a garbage library (usually it is called BL aka Business Layer library) which will hold your entire application classes workload. It is most bloated library which links all adapters and creates aggregates out of them. Feel free to put anything you like here, just try to abstract out any external dependency. Design patterns should be applied here aggressively.
  3. For endpoints (output, which will link all together in a single business application) create separate library (UI, Web, Console, etc) it will link all BL classes into your view or API endpoints. Nothing except linkage/configuration is here. Feel free to read settings here and configure DI/IoC or any other interraction with environment here is appropriate, but do not put business logic here.

PS Im yet to find where this approach is not working. All projects I worked with so far which are not crumbled, usually split like this.

PS 2 Do not try to split Business Layer into multiple libraries or you will get same problem which isharder to solve later.

Upvotes: 1

Related Questions