Service Lifetimes
Service lifetime determines how long a service instance lives and when it's created.
Singleton
A singleton service is created once and shared across the entire application.
typescript
services.addSingleton<ILogger>(ILoggerToken, Logger);
// All calls return the same instance
const logger1 = await provider.getRequiredService<ILogger>(ILoggerToken);
const logger2 = await provider.getRequiredService<ILogger>(ILoggerToken);
console.log(logger1 === logger2); // trueUse cases:
- Loggers
- Configuration services
- Caches
- Database connections (usually)
Scoped
A scoped service is created once per scope. Each scope gets its own instance.
typescript
services.addScoped<IUserService>(IUserServiceToken, UserService);
// Different scopes get different instances
const scope1 = provider.createScope();
const service1 = await scope1.getRequiredService<IUserService>(IUserServiceToken);
const scope2 = provider.createScope();
const service2 = await scope2.getRequiredService<IUserService>(IUserServiceToken);
console.log(service1 === service2); // falseUse cases:
- Request-scoped services (web applications)
- Unit of Work pattern
- Per-request database contexts
Transient
A transient service is created new every time it's requested.
typescript
services.addTransient<IValidator>(IValidatorToken, Validator);
// Each call creates a new instance
const validator1 = await provider.getRequiredService<IValidator>(IValidatorToken);
const validator2 = await provider.getRequiredService<IValidator>(IValidatorToken);
console.log(validator1 === validator2); // falseUse cases:
- Validators
- Mappers
- Stateless services
- Services that should be fresh each time
Lifetime Rules
Dependency Injection Rules
- Singleton can depend on Singleton
- Scoped can depend on Singleton or Scoped
- Transient can depend on anything
Scope Validation
Enable scope validation to catch lifetime mismatches:
typescript
const provider = services.buildServiceProvider({ validateScopes: true });
// This will throw an error:
// "Cannot inject scoped service 'ILogger' into singleton service 'IUserService'"
services.addSingleton<IUserService>(IUserServiceToken, UserService, [ILoggerToken]);
services.addScoped<ILogger>(ILoggerToken, Logger);Best Practices
- Use Singleton for stateless services - Loggers, configuration, caches
- Use Scoped for request-scoped services - User context, database context
- Use Transient for stateless, lightweight services - Validators, mappers
- Enable scope validation in development - Catch lifetime issues early