11. Service Dependency Injection
Date: 2026-01-07
Status
Accepted
Context
Within the services architecture, services were creating instances of other services internally, leading to:
- Tight coupling between services
- Difficulty in testing (cannot mock dependencies)
- Inconsistent RLS context (each service creates its own prismaRLS)
- Hidden dependencies not visible from service interfaces
This ADR establishes the dependency injection pattern specifically for the services layer.
Examples of problematic patterns within services:
UserFeedbackServicecreatingUsersServicein constructorOrderService.getPaginatedOrderscreatingClientServiceinlineChangeOrderStatusService.changeOrderStatuscreatingOrderAvailabilityServiceinline
Decision
Adopt constructor injection for service-to-service dependencies within the services architecture:
-
Never instantiate services directly within service classes - all service instantiation must occur in:
services/index.ts(factory function)- Test files (
*.test.ts) - Mock files (
__mocks__/)
-
Inject service dependencies via constructor using a deps parameter:
export interface MyServiceDeps {dependentService: DependentService;}constructor(tenantId: string,role: string,decodedToken: RLSJwt,deps: MyServiceDeps,) {this.dependentService = deps.dependentService;} -
Wire dependencies in factory (
createTenantServices):- Create independent services first
- Pass them to dependent services
-
Enforce with ESLint rule
local-rules/no-direct-service-instantiationat error level within service files
Consequences
Benefits
- Testability: Services can be tested with mocked dependencies
- Explicit dependencies: Constructor signature shows all service-to-service dependencies
- Single RLS context: All services share tenant context from factory
- CI enforcement: ESLint rule prevents violations within the services architecture
Risks & Mitigations
- Migration effort: Existing service-to-service instantiation violations must be fixed
- Mitigation: Three known violations identified and migrated as part of this change
- Increased factory complexity: Factory must manage the service dependency graph
- Mitigation: Clear ordering in factory; TypeScript provides type safety
Scope
This ADR applies specifically to the services architecture layer. Service instantiation from outside the services layer (e.g., in resolvers, controllers, or other application layers) is not covered by this ADR and may follow different patterns.