Dependency Injection
DomusJS is built around explicit dependency injection (DI) using tsyringe, a lightweight, TypeScript-friendly inversion of control (IoC) container.
This approach ensures:
✅ Clear separation of concerns.
✅ Easy testing (you can swap dependencies for mocks).
✅ Extensibility (replace or extend services without rewriting internals).
What is Dependency Injection?
Section titled “What is Dependency Injection?”Dependency injection is a design pattern where:
- Objects do not create their own dependencies.
- Instead, dependencies are provided (injected) by an external container.
This makes your system:
- More modular.
- More testable.
- Less coupled.
How DomusJS Uses tsyringe
Section titled “How DomusJS Uses tsyringe”DomusJS configures its services, buses, handlers, and modules through the tsyringe
container.
Example:
import { container } from 'tsyringe';import { InMemoryCommandBus } from '@domusjs/infrastructure';
container.register('CommandBus', { useClass: InMemoryCommandBus,});
When you later resolve:
const commandBus = container.resolve('CommandBus');
You get the same registered instance, fully type-safe.
Why Not Use Global Singletons?
Section titled “Why Not Use Global Singletons?”Using DI allows:
- Isolated instances (if needed).
- Flexible testing setups.
- Cleaner architecture (no hidden globals or magic singletons).
Registering Dependencies per Context
Section titled “Registering Dependencies per Context”Each bounded context (like auth
, videos
, etc.) is responsible for registering its own services in the container.
Example:
container.register<AuthService>('AuthService', { useClass: JWTAuthService,});
This keeps context boundaries clear and avoids leaking dependencies.
Benefits in DomusJS
Section titled “Benefits in DomusJS”- All core modules (auth, jobs, observability, etc.) are injectable.
- Handlers (commands, queries, events) can receive injected services.
- You can replace internal implementations (e.g., swap
PinoLogger
for your custom logger).