Dependency Injection

The backend uses a container-based DI pattern for service wiring, testability, and loose coupling.

Container Assembly

The DI container is created in backend/src/services/create-services.ts. It assembles all services with their dependencies and exposes them through a typed container object.

Injection via Hono Context

Services are injected into Hono route handlers via context variables:

Context VariableAccess PatternContains
c.var.servicesBusiness servicesAll domain services (46)
c.var.containerInfrastructureinfrastructure, crossCutting

Route Access (MANDATORY)

// Always destructure directly
const { documentRequests, portal } = c.var.services;
const { infrastructure } = c.var.container;
 
// NEVER alias
// const svc = c.var.services.documentRequests;

Interface Contracts

Each service implements an interface from backend/src/interfaces/ (48 files):

InterfaceService
buildings-service.tsbuildings-service.ts
quote-service.tsquote-service.ts
contact-service.tscontact-service.ts
service-container.tsContainer type definition
database-client.tsDB connection interface
cache-client.tsRedis/cache interface
storage-client.tsFile storage interface

Infrastructure interfaces (database-client.ts, cache-client.ts, storage-client.ts, logger-factory.ts) enable swapping implementations between environments.

Testing Setup

Tests MUST set both container and services on the Hono context:

c.set('container', container);
c.set('services', container.services);

Missing either will cause runtime errors in route handlers.

Benefits

  • Testability — mock any service via its interface
  • Reuse — services work in routes AND background job processors
  • Loose coupling — services depend on interfaces, not implementations
  • Single assembly pointcreate-services.ts is the only place dependencies are wired

See Also