Generate application layer use cases and event listeners with proper DI and orchestration...
Generate application layer use cases and event listeners with proper dependency injection, domain event emission, and orchestration logic.
Creates application layer components that orchestrate domain logic:
execute() methodUse when you need to:
Examples:
import { inject, injectable } from 'tsyringe';
import type { IEntityRepository } from '@/contexts/context/domain';
import { EntityNotFoundError } from '@/contexts/context/domain';
import { eventBus } from '@/global/events/event-bus';
import { EntityCreated } from '@/contexts/context/domain';
export interface ExecuteInput {
field1: string;
field2: number;
}
export interface ExecuteOutput {
id: string;
field1: string;
field2: number;
}
@injectable()
export class ActionEntityUseCase {
constructor(
@inject('IEntityRepository')
private readonly entityRepo: IEntityRepository,
@inject(OtherUseCase)
private readonly otherUseCase: OtherUseCase
) {}
async execute(input: ExecuteInput): Promise<ExecuteOutput> {
this.validateInput(input);
const entity = await this.entityRepo.findById(input.id);
if (!entity) {
throw new EntityNotFoundError(input.id);
}
entity.performBusinessLogic(input.field1);
await this.entityRepo.save(entity);
await eventBus.emit('EntityCreated', new EntityCreated(
entity.getId(),
entity.getField1()
));
return this.toOutput(entity);
}
private validateInput(input: ExecuteInput): void {
if (!input.field1) {
throw new InvalidDataError('Field1 required');
}
}
private toOutput(entity: Entity): ExecuteOutput {
return {
id: entity.getId(),
field1: entity.getField1(),
field2: entity.getField2(),
};
}
}
import { inject, injectable } from 'tsyringe';
import type { EntityCreated } from '@/contexts/context/domain';
import type { IRelatedRepository } from '@/contexts/related/domain';
@injectable()
export class EntityCreatedListener {
constructor(
@inject('IRelatedRepository')
private readonly relatedRepo: IRelatedRepository
) {}
async handle(event: EntityCreated): Promise<void> {
try {
await this.performAction(event);
} catch (error) {
console.error('Error handling EntityCreated:', error);
}
}
private async performAction(event: EntityCreated): Promise<void> {
// Listener logic
}
}
Repositories: Inject by string token
constructor(
@inject('IUserRepository')
private readonly userRepo: IUserRepository
) {}
Use Cases: Inject by class
constructor(
@inject(OtherUseCase)
private readonly otherUseCase: OtherUseCase
) {}
Services: Inject by token or class depending on type
@injectable()
export class CreateEntityUseCase {
constructor(
@inject('IEntityRepository')
private readonly repo: IEntityRepository
) {}
async execute(input: CreateInput): Promise<CreateOutput> {
this.validateInput(input);
const entity = Entity.create({
field1: input.field1,
field2: input.field2,
});
await this.repo.save(entity);
await eventBus.emit('EntityCreated', new EntityCreated(
entity.getId()
));
return this.toOutput(entity);
}
}
@injectable()
export class FindEntityUseCase {
constructor(
@inject('IEntityRepository')
private readonly repo: IEntityRepository
) {}
async execute(id: string): Promise<FindOutput> {
const entity = await this.repo.findById(id);
if (!entity) {
throw new EntityNotFoundError(id);
}
return this.toOutput(entity);
}
}
@injectable()
export class UpdateEntityUseCase {
constructor(
@inject('IEntityRepository')
private readonly repo: IEntityRepository
) {}
async execute(input: UpdateInput): Promise<UpdateOutput> {
const entity = await this.repo.findById(input.id);
if (!entity) {
throw new EntityNotFoundError(input.id);
}
entity.updateField(input.field);
await this.repo.save(entity);
await eventBus.emit('EntityUpdated', new EntityUpdated(
entity.getId()
));
return this.toOutput(entity);
}
}
@injectable()
export class DeleteEntityUseCase {
constructor(
@inject('IEntityRepository')
private readonly repo: IEntityRepository
) {}
async execute(id: string): Promise<void> {
const exists = await this.repo.exists(id);
if (!exists) {
throw new EntityNotFoundError(id);
}
await this.repo.delete(id);
await eventBus.emit('EntityDeleted', new EntityDeleted(id));
}
}
MUST DO:
@injectable() decoratorexecute() methodMUST NOT:
@injectable() decorator/src/contexts/{Context}/application/
āāā usecases/
ā āāā create-{entity}.usecase.ts
ā āāā find-{entity}.usecase.ts
ā āāā find-all-{entity}.usecase.ts
ā āāā update-{entity}.usecase.ts
ā āāā delete-{entity}.usecase.ts
āāā listeners/
ā āāā {event}.listener.ts
āāā index.ts
// application/index.ts
export * from './usecases/create-entity.usecase';
export * from './usecases/find-entity.usecase';
export * from './usecases/update-entity.usecase';
export * from './usecases/delete-entity.usecase';
export * from './listeners/entity-created.listener';
Add to /src/main.ts:
import { EntityCreatedListener } from '@/contexts/entity/application';
const listener = container.resolve(EntityCreatedListener);
eventBus.on('EntityCreated', listener.handle.bind(listener));
After generation, verify:
@injectable()execute() method present@injectable()handle() method