Overview
Dependency Injection for NoEgo Applications
@noego/ioc is a dependency injection container for TypeScript applications. It constructs services, controllers, repositories, and runtime collaborators from their explicit class dependencies โ no manual wiring, no factory functions, no new chains.
Why dependency injection? Without a container, every class that needs a collaborator must construct it โ or accept it already constructed. The first approach ties classes to specific implementations. The second pushes the wiring problem up the call stack until it lands in application bootstrap code that is hard to test and hard to change.
A container inverts that. Classes declare what they need (dependencies), not how to get them. The container resolves the full graph, respects lifetime rules, and gives every test a clean seam to replace collaborators without touching production code.
Why @noego/ioc? Built for the NoEgo ecosystem with a sync-first resolution model, explicit decorators, scoped containers via container.extend(), and a testing philosophy that treats the container as the natural boundary โ not mocking libraries.
Quick Start
import { Component, Inject, LoadAs } from "@noego/ioc";
@Component({ scope: LoadAs.Singleton })
export class GreetingService {
greet(name: string): string {
return `Hello, ${name}!`;
}
}
@Component({ scope: LoadAs.Singleton })
export class HomeController {
constructor(
@Inject(GreetingService) private greeting: GreetingService,
) {}
handle(name: string): string {
return this.greeting.greet(name);
}
}
// Resolution
const container = createContainer();
const ctrl = await container.instance(HomeController);
ctrl.handle("World"); // "Hello, World!"What @noego/ioc Gives You
Declarative Dependency Graphs
Classes declare their dependencies with decorators. The container resolves the full dependency graph โ no manual wiring, no factories, no 'new' chains.
Lifetime Control
Choose singleton, scoped, or transient per class. Singletons for stateless services, scoped for request state, transient for builders. Child containers via extend() for isolation.
Testability as a Design Principle
Constructor injection creates a natural seam. Replace collaborators via container registration in tests โ no mocking framework, no module-level mocks, no surprises.
Documentation
Components & Injection
@Component, @Inject, Parameter.create() โ declare classes and their dependencies.
Lifetimes
Singleton, Scoped, Transient โ choose how long an instance should live.
Resolution
Runtime values with Parameter tokens, runtime implementation selection with SCOPED_CONTAINER.
Testing
Container-based testing patterns โ fakes, scoped isolation, co-located mocks.
Where @noego/ioc Fits in the Stack
The IoC container is the backbone of every NoEgo application. Every package in the ecosystem depends on it for wiring services, controllers, and repositories together.
HTTP controllers receive their IoC-managed services through constructor injection.
Page controllers and load functions are registered as IoC components.
The application bootstrapper configures the root IoC container.
Repositories consume the database connection from the IoC container.
Core Rules
- Mark every managed class with @Component and an explicit scope.
- Use @Inject(token) on every constructor parameter โ explicit tokens are more reliable than reflected metadata.
- Use abstract classes (not interfaces) as injection tokens.
- Prefer Singleton for stateless services; use Scoped for request-specific state.
- Create a fresh container per test; register fakes before resolving the class under test.
- Use Parameter.create() for runtime values, not for wiring services together.