Skip to content

Dependency injection ​

Handlers need dependencies such as repositories and API clients. This page covers the two ways to give them those: closures, which need nothing extra, and an adapter to an IoC container, for class handlers and workflows.

With closures ​

A function handler gets its dependencies from the scope it's declared in. To swap them in tests, declare the handler in a function that takes them:

ts
interface PaymentGateway {
  charge(creditCardToken: string, amount: number): Promise<void>
}

// Dependencies reach a function handler through a closure
export const chargeCreditCardHandler = (gateway: PaymentGateway) =>
  handlerFor(ChargeCreditCard, async command =>
    gateway.charge(command.creditCardToken, command.amount)
  )

A handler doesn't need the bus injected to send or publish: it uses its HandlerContext, which is bound to the bus that received the message.

With a container ​

Class handlers and class workflows are constructed for each message. Without a container, the bus constructs them with new and no arguments, and build() throws ContainerNotRegistered for a class handler whose constructor takes some.

ts
export class ChargeCreditCardHandler implements Handler<ChargeCreditCard> {
  constructor(private readonly gateway: PaymentGateway) {}

  get messageType() {
    return ChargeCreditCard
  }

  async handle(command: ChargeCreditCard) {
    await this.gateway.charge(command.creditCardToken, command.amount)
  }
}

To construct them with their dependencies, pass an adapter to your IoC container with withContainer(). It has one method, get, which returns an instance of the class it's given. This example works with inversify, or any container that resolves instances by class:

ts
const bus = Bus.configure()
  .withMessageTypes(messageTypes)
  .withHandler(ChargeCreditCardHandler)
  .withContainer({
    get: <T>(type: ClassConstructor<T>) => container.get<T>(type)
  })
  .build()

get is also given the message being handled and its attributes, for containers that resolve differently per message, and may return a promise.

See also ​

Released under the MIT License.