6

In a legacy project's service layer, there are tens of service classes and among them there is one service, UtilityService using 2 other services:

class UtilityService{
    private UserService userService;
    private ContactService contactService;
    //some methods using userService and contactService
}

This UtilityService is used in the controller level classes with other services. Some controller classes have depednencies on both UtilityService and UserService since some methods in UserService are needed in the controller class but they are not delegated to UtilityService:

class CustomerController {
    private UserService userService;
    private UtilityService utilityService;
    public List<Customer> getAvailableCustomers() {
      //here both userService and utilityService are in use
    }

}

IMO, the dependencies on both UtilityService and ContactService in one controller class are subject to circular references => Question: how to improve the code structure so that circular references can be easily avoided?

Rui
  • 1,935

2 Answers2

36

The premise of this question is wrong - the example does not show a circular dependency.

The dependencies are

       CustomerController 
       |              |
       V              |
    UtilityService    |
       |     |        |
       |     V        V   
       |     UserService
       V
      ContactService

This is a directed acyclic graph - there are no cycles involved.

The fact the transitive dependency from CustomerController to UserService by UtilityService manifests itself explicitly as a direct dependency is not an issue, as long as it is not a design goal to make UtilityService a facade for accessing the service layer by the controller layer. The examples you gave show no indication for this.

For code, however, which really contains circular dependencies, have a look at this older SWE.SE question: How to solve circular dependency?

This answer shows how to resolve any kind of cyclic dependency by introducing interfaces. This answer shows how cyclic dependencies can be avoided in certain cases by creating smaller components with less responsibilities.

Note also resolving cyclic dependencies is not an end in itself, it is means to an end. A certain amount of cyclic dependencies (within one layer of a layered architecture, of course) can be acceptable, as long as testability and build times are not affected too much.

Doc Brown
  • 218,378
-8

One of the easiest ways is to make sure that you have a single direction to your references between layers. ie

Presentation -> Controllers -> Services -> Data

In your example you have one service reference another. BAD! you could move the logic up to a controller or add another layer

Presentation -> Controllers -> Application -> Services -> Data

Edit: Obviously, your question doesn't technically show a "circular reference" I am assuming you want a solution to the situation you show in your code examples, where you have unknown numbers of instances of the same service and potentially infinite loops or problems with instantiation where ServiceA calls ServiceB calls ServiceA again etc

Ewan
  • 83,178