Dependency Injection (DI) is that shining knight π‘οΈ we bring into our applications to slay the dragon π of tight coupling. It's the poster child of clean architecture β enabling testability, maintainability, and flexibility. Whether you're working on a humble side project or an enterprise-grade spaghetti-monolith π disguised as a microservice system, DI is your go-to wingman.
Butβ¦ π
There's always a "but," isn't there?
Ah yes β circular dependencies π. The software version of a toxic relationship where two services just can't live without each otherβ¦ literally. They keep calling each other in the constructor, stuck in an eternal loop of dependency doom π.
This morning, I had the pleasure (read: horror) π± of facing this exact situation in my own project. And like any good software drama π, it started with something that looked innocent:
public class WhatsappService : IWhatsappService
{
public WhatsappService(IQmsService qmsService, ...)
{
...
}
}
public class QmsService : IQmsService
{
public QmsService(IWhatsappService whatsappService, ...)
{
...
}
}
Now, unless your brain is powered by recursion like a Turing machine, you'll see the problem here: WhatsappService
needs QmsService
, and QmsService
needs WhatsappService
. It's like Romeo and Juliet but with infinite constructor calls. π
The Error β
Autofac, being the strict matchmaker it is, threw this tantrum π€:
A circular dependency was detected for the service of type...
Of course it did. Because while love may be blind π, dependency injection is not.
The Sarcastic Real-World Analogy π
Imagine two people: Alice π© and Bob π¨.
- Alice says: "I can't start working until Bob gives me his contact details."
- Bob says: "I can't give my contact details until Alice starts working first."
Now watch them sit there forever, each waiting for the other to go first. That's exactly what happens with circular dependencies - each service is waiting for the other to be created first, but neither can be created without the other! π
What I Didn't Want
- I didn't want Lazy hacks β feels like saying "I'll talk to you later" in a codebase where time is an illusion.
- I didn't want property injection, which basically whispers, "I might or might not be initialized⦠good luck!" into your code.
- I wanted clean, DI-compliant, testable, stable architecture. You know, like a responsible adult.
The Real Solution: Break the Cycle with a Mediator (No, Not Therapy)
Enter the Mediator Pattern β not the kind you call for roommate fights, but the one that acts as a neutral middle layer.
In my case, I created a new service that neither depends on WhatsappService
nor QmsService
. Instead, it holds the shared logic and is injected into both, thus breaking the dependency loop.
New Setup:
public class WhatsappQmsMediatorService : IWhatsappQmsMediatorService
{
// Shared logic goes here
}
public class WhatsappService : IWhatsappService
{
public WhatsappService(IWhatsappQmsMediatorService mediator, ...)
{
...
}
}
public class QmsService : IQmsService
{
public QmsService(IWhatsappQmsMediatorService mediator, ...)
{
...
}
}
This way, WhatsappService
and QmsService
can live happily ever after without obsessing over each other. The circular dependency is gone. Peace is restored in the DI kingdom.
The Autofac Gotcha
If you're using Autofac with:
builder.RegisterAssemblyTypes(Assembly.Load("MyProject.Services"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope()
.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
Just know this allows circular property injection β not constructor-based. So if your services are stuck in a constructor hug, this won't help. You'll still need the mediator pattern.
Key Takeaways
- Circular dependencies = bad architecture smells. They sneak in like a bug and grow into a monster.
- Constructor injection is safest, most testable β but you must avoid mutual dependency.
- Use the Mediator Pattern when two services need to interact without becoming each other's emotional support class.
- Avoid Lazy and Property injection unless absolutely necessary (and only after lighting a candle and praying to the code gods).
Top comments (0)