Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Service Providers

A ServiceProvider is the unit of wiring — a class that encapsulates registration and boot for one feature area. Every service you want in the container gets its own provider.

Basic structure

import { ServiceProvider, type ServiceProviderContext } from "@raubjo/architect-core"

export class AnalyticsProvider extends ServiceProvider {
  register({ container }: ServiceProviderContext): void {
    container.singleton(AnalyticsService, AnalyticsService)
  }

  boot({ container }: ServiceProviderContext): void | (() => void) {
    const analytics = container.make(AnalyticsService)
    analytics.start()

    return () => analytics.stop()
  }
}

The register / boot contract

register() must only bind into the container — never resolve. boot() may safely resolve any binding because all providers’ register() calls have completed first.

// ✅ correct
register({ container }) {
  container.singleton(MyService, MyService)
}

// ❌ wrong — resolving in register() risks getting undefined
//    if another provider hasn't registered yet
register({ container }) {
  const config = container.make(ConfigRepository) // don't do this
}

Returning a cleanup function

Both register() and boot() can return a cleanup function. The Application collects these and calls them in reverse order on shutdown.

boot({ container }) {
  const poller = container.make(PollingService)
  const interval = setInterval(() => poller.tick(), 5000)

  return () => clearInterval(interval)
}

This matches the same convention as React’s useEffect, Svelte’s onDestroy, and Vue’s onUnmounted — providers that don’t need cleanup simply return nothing.

Provider ownership

Each ServiceProvider is the sole owner of registration, booting, and cleanup for its feature area. No other code should bind or unbind what a provider manages.

Passing providers

Pass provider instances to withProviders():

Application.configure()
  .withProviders([
    new DatabaseProvider(),
    new AuthProvider(),
    new ApiProvider(),
  ])
  .run()

Providers run in the order given.