📖 Business
Pure DI Before Containers
Pure DI means composing your application's object graph entirely by hand — using `new` statements in the Composition Root — without any DI container library. Van Deursen and Seemann argue this should be your starting point and, for many applications, your permanent approach. A DI container is a tool that automates object graph construction through configuration and convention, but that automation comes at a cost: reduced compile-time safety, harder-to-follow wiring logic, and a dependency on a third-party library that becomes load-bearing infrastructure. The book's core thesis is that DI is a set of principles and patterns, not a framework feature. You should be able to practice DI with nothing more than constructors and `new`.
2
Minutes
2
Concepts
+45
XP
1
How It Works
- Pure DI in practice — The Composition Root explicitly constructs every object in the dependency graph:
csharp
// Pure DI — everything is visible, compile-time checked
var logger = new FileLogger("app.log");
var repository = new SqlOrderRepository(connectionString);
var validator = new OrderValidator();
var service = new OrderService(repository, validator, logger);
var controller = new OrderController(service);
Every dependency is explicit. If you rename a constructor parameter or add a new dependency, the compiler tells you immediately.
- When containers earn their keep — Containers become valuable when the object graph is large (hundreds of registrations), when convention-based registration saves significant boilerplate (register all
IHandler<T>implementations automatically), or when advanced lifetime management (scoped, per-request) would require substantial manual plumbing. These thresholds are higher than most teams assume.
- The auto-registration trade-off — Containers offer convention-based registration (
services.AddScoped(typeof(IRepository<>), typeof(Repository<>))), which eliminates repetitive wiring. The cost is that the wiring becomes invisible — adding a new implementation "just works" but debugging why the wrong implementation was resolved requires understanding the container's scanning rules.
- Compile-time vs. runtime errors — Pure DI catches wiring mistakes at compile time. Container-based DI defers many errors to runtime (missing registrations, ambiguous matches, lifestyle mismatches). Some containers mitigate this with verification methods, but none match the compiler's guarantees.
- The progression path — Start with Pure DI. When the Composition Root becomes unwieldy (50+ registrations with repetitive patterns), evaluate whether a container's convention-based registration would reduce genuine duplication. Adopt the simplest container that solves your specific pain, not the most feature-rich one.