Arquitetura DDD

Camadas do Koala Nest e como a requisição percorre o sistema.

O Koala Nest organiza o código em camadas com responsabilidades claras. Cada camada depende apenas das camadas internas, mantendo o domínio isolado de detalhes de infraestrutura e transporte HTTP.

Camada Pasta Responsabilidade
Host src/host Entrada HTTP: controllers, filtros, OpenAPI, módulos Nest
Application src/application Casos de uso: handlers, validators, requests/responses, mapeamentos
Domain src/domain Regras e modelos: entidades, DTOs de consulta, contratos de repositório
Infra src/infra Implementações concretas: TypeORM, repositórios, serviços externos
Core src/core Utilitários transversais: env, bases, ferramentas de mapeamento

O exemplo abaixo ilustra o caminho de POST /person:

mermaid
sequenceDiagram
  participant C as CreatePersonController
  participant H as CreatePersonHandler
  participant V as CreatePersonValidator
  participant M as AutoMapper
  participant R as PersonRepository

  C->>H: handle(request)
  H->>V: validate()
  V-->>H: CreatePersonRequest
  H->>M: map(request, Request, Person)
  H->>R: save(person)
  R-->>H: Person
  H->>M: map(person, Person, Response)
  H-->>C: CreatePersonResponse

O domínio define contratos abstratos; a infraestrutura fornece implementações concretas. O handler depende de IPersonRepository, não da classe TypeORM:

typescript
// src/domain/repositories/iperson.repository.ts
export abstract class IPersonRepository {
  abstract findMany(query: PersonQueryDto): Promise<ListResponse<Person>>;
  abstract findById(id: number): Promise<Person | null>;
  abstract save(person: Person): Promise<Person>;
  abstract delete(person: Person): Promise<void>;
}
typescript
// src/infra/repositories/repository.module.ts
@Module({
  imports: [DatabaseModule],
  providers: [{ provide: IPersonRepository, useClass: PersonRepository }],
  exports: [DatabaseModule, IPersonRepository],
})
export class RepositoryModule {}

A cadeia de imports conecta host → application → infra:

typescript
// src/host/controllers/common/controller.module.ts
@Module({
  imports: [InfraModule],
  providers: [MappingProvider],
  exports: [InfraModule],
})
export class ControllerModule {}
typescript
// src/host/controllers/person/person.module.ts
@Module({
  imports: [ControllerModule],
  controllers: [
    CreatePersonController,
    ReadPersonController,
    ReadManyPersonController,
    UpdatePersonController,
    DeletePersonController,
  ],
  providers: [
    CreatePersonHandler,
    ReadPersonHandler,
    ReadManyPersonHandler,
    UpdatePersonHandler,
    DeletePersonHandler,
  ],
})
export class PersonModule {}