NestJS
Each bounded context is a NestJS module. Ports and repositories are the injection tokens.
Register the handlers
A handler has no @Injectable(): register it with a factory, listing its constructor tokens in order.
ts
providers: [
{ provide: Orders, useClass: PgOrders },
{ provide: Clock, useClass: SystemClock },
{ provide: IdGenerator, useClass: RandomIdGenerator },
{
inject: [Orders, Clock, IdGenerator],
provide: PlaceOrderHandler,
useFactory: (orders: Orders, clock: Clock, ids: IdGenerator) => new PlaceOrderHandler(orders, clock, ids),
},
],Adapters are outside the application: they may carry @Injectable(), as PgOrders does here.
Or use @Injectable() in the application
To list handlers as plain providers, allow Injectable, and only it, in the application:
ts
applicationDependencies: { "@nestjs/common": ["Injectable"] },ts
@Injectable()
export class PlaceOrderHandler extends CommandHandler<PlaceOrder, void, PlaceOrderError> { … }ts
providers: [{ provide: Orders, useClass: PgOrders }, PlaceOrderHandler],WARNING
- The application then depends on NestJS, and needs
emitDecoratorMetadata: Vitest and other esbuild-based tools need a SWC transform. - Import the injected classes as values, not with
import type.
Connect two bounded contexts
A module exports its open host services, and nothing else. The downstream module imports it to build its anti-corruption layer.
ts
@Module({ exports: [CatalogApi], providers: [CatalogApi, …] })
export class CatalogModule {}ts
@Module({ imports: [CatalogModule], providers: [{ provide: PriceList, useClass: CatalogPriceList }, …] })
export class OrderingModule {}Troubleshooting
Nest can't resolve dependencies of PlaceOrderHandler (?, …): the handler is listed as a plain class without @Injectable(), or an injected class is imported with import type, or a token of inject has no provider.