Recently I asked a question about software architecture

Should service call another service or repository directly?

After that answer, I reorganized and refactored my application. In a simple way, my services call each other, StudentService requires ClassService (e.g. to get average class score) and ClassService requires StudentService (to get students assigned to the class). The classes (simplified) are shown bellow:

public class StudentService : IStudentService{protected readonly IClassService ClassService;public StudentService(IClassService classService){ClassService = classService;}}public class ClassService : IClassService{protected readonly IStudentService StudentService;public ClassService(IStudentService studentService){StudentService = studentService;}}

Services are registered in DI container in .NET Core

services.AddTransient<IStudentService, StudentService>();services.AddTransient<IClassService, ClassService>();

During resolve

var studentService = app.ApplicationServices.GetService<IStudentService>();

I get an Exception A circular dependency was detected for the service of type...

I understand the implementation problem here, but I don't know how to fix the architecture problem here.

Could you provide some suggestion?

Edit:Ok, I have more realistic case, e.g. Employees, Services and Companies.We have Repository Layer with abstract generic CRUD repository. Then we have derived classes: EmployeesRepository ServicesRepository and CompaniesRepository.EmployeesRepository implements methods: GetTopEmployeesOfTheMonthServicesRepository implements methods: GetTopServicesForEmployeesCompaniesRepository implements methods: GetCompaniesWithTopIncome

On layer above, (let's call it Business Layer) we have the same structure: abstract generic CRUD Helper, that e.g. checks user privileges and calls methods from CRUD repository. Then we have derived EmployeesHelper, ServicesHerlper and CompaniesHelper. All of them checks user privileges and calls methods from proper repository (EmployeesHelper from EmployeesRepository etc).Moreover, on this layer we have methods for creating more "complex" objects - the objects that are composed from many entities.For example, CompaniesHelper has method to show top five companies with most sold services. The data will be shown on one screen so it should be generated by one API request and returned as JSON.The ShowCompaniesWithServices from CompaniesHelper method calls CompaniesHelper methods and EmployeesHelper methods. On the second side, we have EmployeesHelper, that implements method to return complex object with top employees of the month, their top services and companies that they work, so it needs Comapnies Helper.

How to solve this circular dependency? Is there any Design Pattern to resolve it?

5

Best Answer


You have two ways:

  1. Write code such that it isn't needed to call ClassService from StudentService (or StudentService from ClassService)

  2. Create third class:

    public class StudentService: IStudentService{private readonly IClassSharedSerive _classSharedService;...}public class ClassService: IClassService{private readonly IClassSharedSerive _classSharedService;private readonly IStudentService _studentService;...}public class ClassSharedService: IClassSharesService{... without references to IClassService and IStudentService}

But, in most cases it is needed to write correctly StudentService and ClassService (way 1)

I'm not completely sure that the way I went around this is very clean, but it works just fine for me. When I hit that circular dependency problem, I removed the service from the Dependency Injection and I simply passed the service as a parameter to the function that needed it.

So when you call the method it looks like this:

// Inside the Student servicevar result = _classService.GetClassStudents(classId, this)

Might not work for everyone, but in my case I had a pretty simple setup, so I did'nt dig deeper.

Hope this helps.

So, I don't believe that services should have other services injected.

I think that each service should be able to stand on its own and provide just the data that it is responsible for. The consumer of the IStudentService can also be a consumer of the IClassService if it needs data from both sources.

I'm not sure if this answers the question, but I have a Service Wrapper which wraps all the interfaces of IOC into one interface.

In each individual service I will pass individual required services, logging, repository, emailSender etc.

EmailSender relies on IRepository for example.

So IServiceWrapper is my parent Interface and this prevents circular dependency, I only use the service wrapper for my net core controllers, and use individual Interfaces for services.

I used yet another workaround: Lazy<T>.

public class StudentService : IStudentService{private readonly IServiceProvider _serviceProvider;protected Lazy<IClassService> _lazyClassService;public StudentService(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;_lazyClassService = new Lazy<IClassService>(() =>_serviceProvider.GetRequiredService<IClassService>());}public SomeInfo GetSomeClassInfo(string studentId){return _lazyClassService.Value.GetFooBar(studentId);}}

It may not be that clean, or a solution for everyone, but worked fine in my relatively simple setup.Note that you don't need the Lazy<T> in the other class ClassService.