Requests e responses

DTOs de entrada e saída com ObjectClass, AutoMap e decoradores Swagger.

A camada application define contratos de entrada e saída para cada operação. Essas classes alimentam tanto o mapeamento quanto a documentação OpenAPI.

Requests de create estendem ObjectClass e usam @ApiProperty() para Swagger e @AutoMap() para mapeamento:

typescript
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[];
}

Responses mínimas retornam apenas o identificador gerado:

typescript
import { CreatedRegistreWithIdResponse } from '@/application/common/created-registre.response';

export class CreatePersonResponse extends CreatedRegistreWithIdResponse {}

Para novos recursos, reutilize CreatedRegistreWithIdResponse ou CreatedRegistreWithUUIDResponse de created-registre.response.ts.

Requests de update são classes planas — não estendem ObjectClass:

typescript
// 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[];
}

Responses de leitura expõem o modelo completo (ou um subconjunto) mapeado da entidade:

typescript
// 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[];
}

Listagens estendem ListResponseBase (já expõe items e count):

typescript
export class ReadManyPersonResponseItem {
  @ApiProperty()
  @AutoMap()
  id: number;

  @ApiProperty()
  @AutoMap()
  name: string;

  @ApiProperty()
  @AutoMap()
  active: boolean;
}

export class ReadManyPersonResponse extends ListResponseBase<ReadManyPersonResponseItem> {}

No handler: ReadManyPersonResponse.from({ items, count }).

Estenda PaginationRequest para herdar parâmetros de paginação:

typescript
export class ReadManyPersonRequest extends PaginationRequest {
  @ApiProperty()
  @AutoMap()
  name?: string;
}

Organize por recurso e operação:

text
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 por especialidade