DIPatterns: {
    command: <T>(handler: new (...args: any[]) => T) => Resolver<T>;
    query: <T>(handler: new (...args: any[]) => T) => Resolver<T>;
    repository: <T>(repo: new (...args: any[]) => T) => Resolver<T>;
    aggregate: <T>(aggregate: new (...args: any[]) => T) => Resolver<T>;
    domainService: <T>(service: new (...args: any[]) => T) => Resolver<T>;
    applicationService: <T>(service: new (...args: any[]) => T) => Resolver<T>;
    factory: <T>(factory: (...args: any[]) => T) => Resolver<T>;
} = ...

DDD/CQRS pattern-based resolvers for Awilix.

Provides opinionated registration patterns for common DDD building blocks. These patterns encode best practices for service lifetimes based on their roles.

Type declaration

  • Readonlycommand: <T>(handler: new (...args: any[]) => T) => Resolver<T>

    Command handler pattern - scoped per request.

    Command handlers should be scoped to ensure they have access to request-specific context while allowing dependency injection.

    container.register({
    createUserHandler: DIPatterns.command(CreateUserHandler),
    updateUserHandler: DIPatterns.command(UpdateUserHandler)
    });
  • Readonlyquery: <T>(handler: new (...args: any[]) => T) => Resolver<T>

    Query handler pattern - scoped per request.

    Query handlers should be scoped for the same reasons as command handlers.

    container.register({
    getUserHandler: DIPatterns.query(GetUserHandler),
    listUsersHandler: DIPatterns.query(ListUsersHandler)
    });
  • Readonlyrepository: <T>(repo: new (...args: any[]) => T) => Resolver<T>

    Repository pattern - singleton.

    Repositories manage data access and should typically be singletons as they often maintain connection pools or caches.

    container.register({
    userRepository: DIPatterns.repository(UserRepository),
    productRepository: DIPatterns.repository(ProductRepository)
    });
  • Readonlyaggregate: <T>(aggregate: new (...args: any[]) => T) => Resolver<T>

    Aggregate root pattern - transient (new instance each time).

    Aggregate roots represent domain entities and should be created fresh for each use to avoid state pollution.

    container.register({
    user: DIPatterns.aggregate(User),
    order: DIPatterns.aggregate(Order)
    });
  • ReadonlydomainService: <T>(service: new (...args: any[]) => T) => Resolver<T>

    Domain service pattern - singleton.

    Domain services encapsulate business logic that doesn't belong to a specific entity. They are typically stateless and can be singletons.

    container.register({
    pricingService: DIPatterns.domainService(PricingService),
    shippingService: DIPatterns.domainService(ShippingService)
    });
  • ReadonlyapplicationService: <T>(service: new (...args: any[]) => T) => Resolver<T>

    Application service pattern - singleton.

    Application services orchestrate use cases and coordinate between domain objects. They are typically stateless and can be singletons.

    container.register({
    userService: DIPatterns.applicationService(UserService),
    orderService: DIPatterns.applicationService(OrderService)
    });
  • Readonlyfactory: <T>(factory: (...args: any[]) => T) => Resolver<T>

    Factory pattern - transient function.

    Factories create instances and should return new instances each time.

    container.register({
    userFactory: DIPatterns.factory((deps) => {
    return (data) => new User(data, deps.validator);
    })
    });
import { createContainer } from '@stratix/runtime';
import { DIPatterns } from '@stratix/di';

const container = createContainer();

container.register({
// Command handlers are scoped per request
createUserHandler: DIPatterns.command(CreateUserHandler),

// Repositories are singletons
userRepository: DIPatterns.repository(UserRepository),

// Aggregates are transient (new instance each time)
userAggregate: DIPatterns.aggregate(User)
});