# Dependency Injection (Part 1: The Container Mechanics)

This topic assumes familiarity with `backend/frameworks/05-controllers` (thin controllers that delegate to services) and previews concepts `architecture/architecture/05-dependency_inversion_and_injection` covers as a general OOP design principle. That architecture topic is about *why* depending on abstractions instead of concrete implementations is good design; this topic is about how a concrete framework — NestJS, as the running example — actually *wires* that dependency graph together at runtime, mechanically.

## 1. The Problem: Manual Wiring Doesn't Scale

Without a DI container, every class that needs a collaborator has to construct it itself, or receive it from whoever constructs *it*:

```ts
class OrderRepository {
  constructor(private db: Database) {}
}

class OrderService {
  constructor(private repo: OrderRepository) {}
}

class OrderController {
  constructor(private service: OrderService) {}
}

// Someone, somewhere, has to do this by hand:
const db = new Database(config);
const repo = new OrderRepository(db);
const service = new OrderService(repo);
const controller = new OrderController(service);
```

This "someone" is called the *composition root*, and in a real application with dozens of services, each depending on several others, hand-writing this wiring becomes its own maintenance burden — every new dependency means updating every constructor call up the chain, by hand, everywhere that class is instantiated.

## 2. What a DI Container Actually Does

A DI container automates exactly this wiring. You declare *what* a class needs (via its constructor signature) and mark classes as injectable; the container inspects the declared dependencies, builds the graph, and instantiates everything in the right order:

```ts
import { Injectable, Module, Controller, Get } from "@nestjs/common";

@Injectable()
class OrderRepository {
  findAll() { return []; }
}

@Injectable()
class OrderService {
  constructor(private repo: OrderRepository) {} // declared, not constructed
  listOrders() { return this.repo.findAll(); }
}

@Controller("orders")
class OrderController {
  constructor(private service: OrderService) {} // also declared, not constructed
  @Get()
  list() { return this.service.listOrders(); }
}

@Module({
  controllers: [OrderController],
  providers: [OrderService, OrderRepository],
})
class OrderModule {}
```

Nothing in this code calls `new OrderService(...)` or `new OrderRepository(...)` anywhere. `@Injectable()` marks a class as something the container *can* construct; listing it in a module's `providers` array tells the container it *should* be available for injection within that module. When NestJS bootstraps `OrderModule`, it reads `OrderController`'s constructor via TypeScript's emitted type metadata, sees it needs an `OrderService`, resolves *that* class's constructor, sees it needs an `OrderRepository`, resolves that (no further dependencies), and only then constructs the whole chain bottom-up: `OrderRepository` first, then `OrderService(repo)`, then `OrderController(service)`.

## 3. Constructor Injection Is the Whole Mechanism

There's no separate "registration" step beyond the `providers` array and no manual `container.get(Foo)` calls scattered through the code (that pattern — a global lookup by type — is called *service location*, and is a different, more error-prone approach the constructor-injection style deliberately avoids). Everything a class needs is declared once, in its constructor parameter list, and the container guarantees every instance it hands out already has its dependencies satisfied:

```ts
@Injectable()
class NotificationService {
  constructor(
    private orderService: OrderService,
    private mailer: MailerService,
  ) {}
}
```

By the time application code receives a `NotificationService` instance — however deep in the graph it sits — `orderService` and `mailer` are already real, fully-constructed objects. No consumer of `NotificationService` needs to know or care how those got there.

## 4. Why This Beats Manual Wiring: Testability

The single biggest practical payoff is substitutability in tests. Because `OrderService` only ever declares "I need something shaped like `OrderRepository`," a test can hand it anything satisfying that shape — including a fake that never touches a real database:

```ts
class FakeOrderRepository {
  findAll() { return [{ id: 1, total: 42 }]; } // no real DB involved
}

const service = new OrderService(new FakeOrderRepository() as any);
console.log(service.listOrders()); // deterministic, no I/O, no test database needed
```

In production, the container supplies the real `OrderRepository`; in a test, the test supplies a fake directly via the constructor — `OrderService` itself never changes and never even knows the difference. This is the direct payoff of the inversion principle covered in `architecture/architecture/05-dependency_inversion_and_injection`: the high-level `OrderService` depends on the *shape* of a repository, not a concrete database-backed implementation, so anything matching that shape is interchangeable.

## Common Pitfalls

1. **Constructing dependencies manually inside a class marked `@Injectable()`.** `constructor() { this.repo = new OrderRepository(db) }` defeats the entire point — the container can no longer substitute a fake in tests, and the class silently owns a concrete dependency instead of an interchangeable one.
2. **Forgetting to list a provider in a module's `providers` array.** The container will throw a "cannot resolve dependency" error at bootstrap — a class can be perfectly `@Injectable()`-decorated and still be unresolvable if the module wiring doesn't know about it.
3. **Confusing DI with service location.** Reaching for a global `container.get(SomeService)` from deep inside unrelated code reintroduces the same tight coupling DI is meant to remove — the *whole* point is that dependencies arrive via the constructor, not via an ambient lookup.
4. **Assuming DI is NestJS-specific magic.** The pattern (declare dependencies via constructor, let a container resolve and construct the graph) is a general technique implemented by many frameworks across many languages — NestJS is simply this topic's concrete running example.

## Quick Reference

```
Composition root  -> where the object graph gets built, by hand, without a container
@Injectable()      -> marks a class as constructible by the container
providers: [...]   -> tells a module's container which injectables it can resolve
Constructor injection -> dependencies declared as constructor params, never `new`'d internally
Container's job    -> read constructor signatures, build the graph, instantiate bottom-up
Payoff             -> swap a real dependency for a fake in tests with zero production code changes
Anti-pattern        -> service location (global `container.get(X)` lookups scattered through code)
```
