Requests and responses
Input and output DTOs with ObjectClass, AutoMap, and Swagger decorators.The application layer defines input and output contracts for each operation. These classes feed both mapping and OpenAPI documentation.
Create request
Create requests extend ObjectClass and use @ApiProperty() for Swagger and @AutoMap() for mapping:
export class CreatePersonAddressRequest extends ObjectClass<CreatePersonAddressRequest> {
@ApiProperty({ example: '123 Main St' })
@AutoMap()
address: string;
}
export class CreatePersonContactRequest extends ObjectClass<CreatePersonContactRequest> {
@ApiProperty({ example: 'john.doe@example.com' })
@AutoMap()
contact: string;
}
export class CreatePersonRequest extends ObjectClass<CreatePersonRequest> {
@ApiProperty({ example: 'John Doe' })
@AutoMap()
name: string;
@ApiProperty({ type: CreatePersonAddressRequest })
@AutoMap()
address: CreatePersonAddressRequest;
@ApiProperty({ type: CreatePersonContactRequest, isArray: true })
@AutoMap({ type: () => CreatePersonContactRequest })
contacts: CreatePersonContactRequest[];
}
Create response
Minimal responses return only the generated identifier:
import { CreatedRegistreWithIdResponse } from '@/application/common/created-registre.response';
export class CreatePersonResponse extends CreatedRegistreWithIdResponse {}
For new resources, reuse CreatedRegistreWithIdResponse or CreatedRegistreWithUUIDResponse from created-registre.response.ts.
Update request
Update requests are plain classes — they do not extend ObjectClass:
// src/application/person/update/update-person.request.ts
export class UpdatePersonRequest {
@ApiProperty({ example: 1 })
@AutoMap()
id: number;
@ApiProperty({ example: 'John Doe' })
@AutoMap()
name: string;
@ApiProperty({ type: () => UpdatePersonAddressRequest })
@AutoMap()
address: UpdatePersonAddressRequest;
@ApiProperty({ type: () => UpdatePersonContactRequest, isArray: true })
@AutoMap({ type: () => UpdatePersonContactRequest })
contacts: UpdatePersonContactRequest[];
}
Read response
Read responses expose the full model (or a subset) mapped from the entity:
// src/application/person/read/read-person.response.ts
export class ReadPersonResponse {
@ApiProperty({ example: 1 })
@AutoMap()
id: number;
@ApiProperty({ example: 'John Doe' })
@AutoMap()
name: string;
@ApiProperty({ type: () => ReadPersonAddressResponse })
@AutoMap()
address: ReadPersonAddressResponse;
@ApiProperty({ type: () => ReadPersonContactResponse, isArray: true })
@AutoMap({ type: () => ReadPersonContactResponse })
contacts: ReadPersonContactResponse[];
}
List response
Listings extend ListResponseBase (already exposes items and count):
export class ReadManyPersonResponseItem {
@ApiProperty()
@AutoMap()
id: number;
@ApiProperty()
@AutoMap()
name: string;
@ApiProperty()
@AutoMap()
active: boolean;
}
export class ReadManyPersonResponse extends ListResponseBase<ReadManyPersonResponseItem> {}
In the handler: ReadManyPersonResponse.from({ items, count }).
List request
Extend PaginationRequest to inherit pagination parameters:
export class ReadManyPersonRequest extends PaginationRequest {
@ApiProperty()
@AutoMap()
name?: string;
}
Folder convention
Organize by resource and operation:
src/application/person/
├── create/ # request, response, handler, validator
├── read/ # handler, response
├── read-many/ # request, response, handler, validator
├── update/ # request, handler, validator
├── delete/ # handler
└── jobs/
├── cron/ # CronJobHandlerBase
└── events/ # EventJob + handlers by specialty