📖 Business
DI Anti-Patterns
Van Deursen and Seemann catalog three destructive patterns that masquerade as dependency injection but actually undermine the goals DI is meant to achieve. Service Locator hides dependencies behind a global registry, making classes appear simple while creating invisible coupling to the locator itself. Ambient Context provides cross-cutting concerns through static accessors (like `DateTime.Now` or `HttpContext.Current`), making code untestable and behavior unpredictable. Constrained Construction forces implementations to share an identical constructor signature, coupling consumers to a specific creation contract rather than to an abstraction. Each anti-pattern shares a common thread: they look convenient locally but create systemic problems — hidden dependencies, test brittleness, and rigid architectures.
2
Minutes
2
Concepts
+45
XP
1
How It Works
- Service Locator — A class calls a static or injected locator to resolve its dependencies at runtime rather than receiving them through its constructor. The critical problem is that the class's true dependencies are invisible from its public API. Consumers see a constructor that takes
IServiceLocator(or nothing at all), but the class might resolve ten services internally. This makes it impossible to know what a class needs without reading its implementation, and unit tests require configuring a fake locator with exactly the right registrations.
csharp
// ANTI-PATTERN: dependencies are hidden
public class OrderService
{
public void PlaceOrder(Order order)
{
var repo = ServiceLocator.Get<IOrderRepository>(); // hidden dependency
var logger = ServiceLocator.Get<ILogger>(); // hidden dependency
// ...
}
}
- Ambient Context — Cross-cutting concerns are accessed through static properties or thread-local storage.
DateTime.Now,HttpContext.Current, and customSecurityContext.CurrentUserpatterns all fall here. The problem is twofold: the dependency is completely invisible (no constructor parameter, no method parameter), and the static nature makes parallel testing unreliable since tests can leak state to each other.
- Constrained Construction — A framework or plugin system requires all implementations to have a specific constructor signature (e.g., a parameterless constructor or a constructor taking exactly
IConfiguration). This prevents implementations from declaring their actual dependencies, forcing workarounds like service location inside the constructor.
- Why these feel right but aren't — Each anti-pattern reduces the ceremony of wiring dependencies. Service Locator requires only one dependency (the locator). Ambient Context requires zero. Constrained Construction simplifies plugin loading. The convenience is real but the cost — hidden coupling, test fragility, and loss of compile-time safety — compounds over time.
- The diagnostic question — For any class, ask: "Can I determine all of this class's dependencies by looking only at its constructor?" If the answer is no, an anti-pattern is likely present.