> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dependency Injection

> Resolve controllers, services, middleware, and repositories through a shared Inversify-based container with scoped lifetimes and constructor injection.

The `@ooneex/container` package is the dependency injection (DI) engine of the framework. It wraps [Inversify](https://inversify.io) behind a small, type-safe API and exposes a single shared `container` singleton. Almost every artifact you build — controllers, [services](/components/service), [middleware](/basics/middleware), and [repositories](/components/repository) — is registered into this container and resolved with its dependencies supplied through the constructor. You rarely call the container directly; the framework's decorators register decorated classes for you, and `@inject` wires the graph together.

## Why dependency injection

* **One shared graph.** A single global `container` instance backs the whole app, so a service resolved in a controller and the same service resolved in middleware share their registration and (for singletons) their instance.
* **Constructor injection.** Declare what a class needs with `@inject(Token)` in its constructor; the container constructs the dependency tree for you instead of `new`-ing collaborators by hand.
* **Scoped lifetimes.** Choose `Singleton`, `Transient`, or `Request` per registration to control whether an instance is shared, recreated each time, or recreated per request.
* **Auto-registration.** Framework decorators (`@decorator.service()`, `@decorator.middleware()`, `@decorator.repository()`, controller routing) register the class as a side effect, so manual `container.add` is the exception, not the rule.
* **Type-safe resolution.** `get<T>` and `getConstant<T>` return typed instances and values, and failures throw a typed `ContainerException`.

## How it works

The package keeps one internal Inversify container (`sharedDI`) that every `Container` instance delegates to. When a class is registered, the container marks it `injectable`, binds it to itself, and applies the chosen scope. When you resolve it, Inversify reads the `@inject` metadata on its constructor and builds the dependency graph.

| Stage        | What happens                                                                                                                              |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Registration | `container.add(MyClass, scope)` binds the class to the shared container and applies its lifetime scope. Decorators do this automatically. |
| Wiring       | `@inject(Token)` on a constructor parameter records which dependency that slot needs.                                                     |
| Resolution   | `container.get(MyClass)` asks Inversify for an instance; it recursively resolves and injects every `@inject` dependency.                  |
| Failure      | If a binding is missing or construction throws, resolution raises a `ContainerException` with a descriptive message and code.             |

The shared instance is exported ready to use:

```typescript theme={null}
import { container } from "@ooneex/container";
```

## The Container class

`Container` implements `IContainer` and forwards to the shared Inversify instance, so creating a new `Container()` and using the exported `container` operate on the same registrations. Use the exported singleton in application code.

| Method           | Signature                                                      | Description                                                                               |
| ---------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `add`            | `add(target: Constructor, scope?: EContainerScope): void`      | Registers a class. Defaults to `EContainerScope.Singleton`. Re-registering rebinds.       |
| `get`            | `get<T>(target: Constructor): T`                               | Resolves an instance, injecting its dependencies. Throws `ContainerException` on failure. |
| `has`            | `has(target: Constructor): boolean`                            | Returns `true` if the class is registered.                                                |
| `remove`         | `remove(target: Constructor): void`                            | Unbinds a registered class (no-op if not bound).                                          |
| `addConstant`    | `addConstant<T>(identifier: string \| symbol, value: T): void` | Stores a constant value under a string or symbol identifier.                              |
| `getConstant`    | `getConstant<T>(identifier: string \| symbol): T`              | Retrieves a constant. Throws `ContainerException` if not found.                           |
| `hasConstant`    | `hasConstant(identifier: string \| symbol): boolean`           | Returns `true` if a constant is registered.                                               |
| `removeConstant` | `removeConstant(identifier: string \| symbol): void`           | Unbinds a registered constant (no-op if not bound).                                       |

```typescript theme={null}
import { container, EContainerScope } from "@ooneex/container";

class UserService {
  public getUsers() {
    return [{ id: 1, name: "John" }];
  }
}

container.add(UserService); // singleton by default
const users = container.get(UserService).getUsers();
```

## Scopes

A scope decides how long a resolved instance lives. Pass an `EContainerScope` value as the second argument to `add` (or as the argument to a framework decorator). The default is `Singleton`.

| Scope     | Enum member                 | Lifetime                                                        | Use it for                                                                |
| --------- | --------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Singleton | `EContainerScope.Singleton` | One instance for the app's lifetime.                            | Stateless services, config, env, loggers, repositories — the common case. |
| Transient | `EContainerScope.Transient` | A new instance on every resolution.                             | Lightweight, per-use helpers where sharing state would be wrong.          |
| Request   | `EContainerScope.Request`   | A new instance per request context, reused within that request. | Per-request state such as a request-scoped logger or unit of work.        |

```typescript theme={null}
import { container, EContainerScope } from "@ooneex/container";

container.add(DatabaseConnection, EContainerScope.Singleton);
container.add(RequestLogger, EContainerScope.Request);
container.add(TemporaryWorker, EContainerScope.Transient);

const a = container.get(TemporaryWorker);
const b = container.get(TemporaryWorker);
console.log(a === b); // false — transient gives a fresh instance each time
```

## Constructor injection with `@inject`

Declare dependencies as constructor parameters and annotate each with `@inject(Token)`, where the token is the class (or a string alias). The container resolves and supplies them when it builds the class. `inject` is re-exported from `@ooneex/container`, so import it from there.

A service injected into a controller — the controller is constructed by the container during routing, so the service arrives ready to use:

```typescript theme={null}
import { inject } from "@ooneex/container";
import type { ContextType, IController } from "@ooneex/controller";
import type { IResponse } from "@ooneex/http-response";
import { Route } from "@ooneex/routing";
import { UserService } from "../services/UserService";

@Route.get("/api/users", {
  name: "api.users.list",
  version: 1,
  description: "List all users",
})
export class UserController implements IController {
  constructor(@inject(UserService) private readonly users: UserService) {}

  public async index(context: ContextType): Promise<IResponse> {
    return context.response.json({ users: this.users.getUsers() });
  }
}
```

Injecting the application environment into middleware — the same pattern the framework uses throughout:

```typescript theme={null}
import { AppEnv } from "@ooneex/app-env";
import { inject } from "@ooneex/container";
import type { ContextType } from "@ooneex/controller";
import { decorator, type IMiddleware } from "@ooneex/middleware";

@decorator.middleware()
export class VersionMiddleware implements IMiddleware {
  constructor(@inject(AppEnv) private readonly env: AppEnv) {}

  public async handler(context: ContextType): Promise<ContextType> {
    context.response.header.set("X-App-Version", this.env.APP_VERSION ?? "unknown");
    return context;
  }
}
```

Dependencies nest: a repository injects a database, a service injects the repository, a controller injects the service, and resolving the controller builds the whole chain.

## Aliases and constants

Beyond classes, the container can resolve values by a string or symbol identifier. This is how the framework exposes shared infrastructure under stable names — for example, repositories inject the active database via the `"database"` alias rather than a concrete class:

```typescript theme={null}
import { inject } from "@ooneex/container";
import type { ITypeormDatabase } from "@ooneex/database";
import { decorator } from "@ooneex/repository";

@decorator.repository()
export class UserRepository {
  constructor(@inject("database") private readonly database: ITypeormDatabase) {}
}
```

Register and read constant values with `addConstant` / `getConstant`. Constants hold any value — strings, objects, or resolved instances — and `get<T>` / `getConstant<T>` return them typed:

```typescript theme={null}
import { container } from "@ooneex/container";

container.addConstant("app.name", "My Application");
container.addConstant<{ debug: boolean }>("app.config", { debug: true, maxConnections: 100 });

const name = container.getConstant<string>("app.name"); // "My Application"
const config = container.getConstant<{ debug: boolean }>("app.config");
console.log(config.debug); // true
```

Use a `symbol` identifier when you need a collision-proof token; use a string alias when readability and cross-package convention (like `"database"` or `"mailer"`) matter more.

## Auto-registration by the framework

You almost never call `container.add` yourself. Each framework decorator registers its class into the shared container as a side effect of being applied, using the scope you pass (default singleton). After that, resolving the class — or injecting it elsewhere — just works.

| Decorator                 | Registers                    | Resolved by                                           |
| ------------------------- | ---------------------------- | ----------------------------------------------------- |
| `@decorator.service()`    | A service class              | Injected into controllers, middleware, other services |
| `@decorator.middleware()` | An HTTP or socket middleware | The request pipeline before the controller            |
| `@decorator.repository()` | A repository class           | Injected into services                                |
| Controller routing        | A controller class           | The router when a route matches                       |

```typescript theme={null}
import { decorator } from "@ooneex/service";

@decorator.service()
export class PaymentService {
  public process(amount: number) {
    // ...
  }
}

// No manual container.add — the decorator already registered it.
// Injecting PaymentService anywhere now resolves this class.
```

The decorator-driven base `injectable` helper accepts a scope, so `@decorator.service(EContainerScope.Transient)` and similar control lifetime without ever touching the container API directly.

## Error handling

When a binding is missing or a dependency cannot be constructed, resolution throws a `ContainerException` carrying a message and an error code (`SERVICE_RESOLVE_FAILED` for `get`, `CONSTANT_RESOLVE_FAILED` for `getConstant`). Catch it to distinguish DI failures from other errors:

```typescript theme={null}
import { container, ContainerException } from "@ooneex/container";

try {
  const service = container.get(UnregisteredService);
} catch (error) {
  if (error instanceof ContainerException) {
    console.error("Resolution failed:", error.message);
  }
}
```

A common cause is a forgotten decorator: if a class is injected but never registered, the framework cannot resolve it. Use `container.has(MyClass)` to confirm registration while debugging.

## Best practices

* **Inject, don't `new`.** Pull collaborators through the constructor with `@inject` so they are resolved, scoped, and testable — never instantiate them by hand inside methods.
* **Let decorators register.** Prefer `@decorator.service()`, `@decorator.middleware()`, and `@decorator.repository()` over manual `container.add`; reach for `add` only for values the framework does not register for you.
* **Default to singleton.** Most services are stateless and belong as singletons; choose `Transient` or `Request` only when a fresh or per-request instance is genuinely required.
* **Depend on tokens, not internals.** Inject a class or a stable alias (like `"database"`) rather than reaching into another module's globals, so the dependency graph stays explicit.
* **Use typed generics.** Call `get<T>` and `getConstant<T>` with the expected type so the compiler checks how you use the result.
* **Handle `ContainerException`.** Wrap resolution that may fail and check `instanceof ContainerException` to separate DI errors from application errors.

## Related

* [Services](/components/service) — the unit of business logic resolved through the container.
* [Repositories](/components/repository) — data-access classes that inject the database via DI.
* [Middleware](/basics/middleware) — pipeline classes constructed with injected dependencies.
* [Controllers](/basics/controller) — route handlers the container builds with their services.
