First time i encounter Circular dependency

1. The problem

In the last semester while working on my Laravel project, i encountered a problem which led to a memory leak whenever i call an API that interacts with my OrderService class:

class OrderService {
    public function __construct(private MomoService $momoService, private VnPayService $vnpayService){}
}

In the above code, class OrderService injects two dependencies MomoService and VnPayService by using constructor injection method (this is dependency innjection concept). Whenever i call OrderService, Laravel will look in its Service Container and find or create the instance of each MomoService and VnPayService. This process is called Inversion of control, i won't have to write something like MomoService ms = new MomoService(). The VnPayService looks like this:

class VnPayService {
   public function __constructor(private OrderService $orderService){}
}

VnPayService is bound to OrderService. This is where the memory leak occured. My code creates a loop when OrderService depends on VnPayService and VnpayService depends on OrderService. The result is when my API calls OrderService, OrderService calls VnPayService, VnPayService calls OrderService and BOOM my laptop freezes due to memory leak.

This problem is called Circular Dependency or Circular Reference. A circular dependency occurs when two or more components or classes depend on each other or depend on itself.

2. How do i fix the problem

Honestly, i currently don't have enough knowledge and experience for this kind of design pattern problem :"). So hopefully i will have the ability to solve this in the near future.

After i figured out what caused my laptop freeze, here are some thoughts that come first in my mind to solve the problem:

  • Do i need to separate VnPay and Momo services from my OrderService?

Each VnPay and Momo service contains about 150 lines of code, so i think it is a good practice to make these two become independence. While this project is relatively small and just for university, i want myself to be ready for real world problems. Payment service is much more complicated in real life (i supposed). But in the end, i choose to merge those services to one OrderService because i don't have time : ).

  • My plan

Encountering this problem has motivated me to dive into world of design patterns. I think my problem is too small to apply a pattern or any real world concepts for it. But it's worth to give it a try in design pattern field. I plan to gain a basic understanding of design pattern and do more projects, especially more complicated back-end services.

Remember that problems come first, not patterns come first. In my opinion, the best way to learn design pattern is just create more complex problems and then start refactoring to apply the appropriate patterns.

3. Nice topic to read

Vietnamese- A student is dealing with a circular dependency issue in his university project