Arquitectura
Arquitectura hexagonal / clean por módulo de negocio. El dominio no depende de Nest, HTTP ni infraestructura.
text
src/
├── ApiModule.ts # composición raíz (+ EventBusModule, EventEmitter)
├── main.ts # bootstrap Nest
├── config.ts # env (incl. TEST_MODE_ENABLED)
├── shared/ # piezas transversales
│ ├── domain/
│ │ ├── errors/ # DomainError + DomainErrorCode + errores compartidos
│ │ ├── event-bus/ # puerto EventBus + EVENT_BUS_TOKEN
│ │ ├── hasher/ # puerto HasherService + HASHER_SERVICE_TOKEN
│ │ ├── events/ # AggregateRoot, DomainEvent, DomainEventId/Name
│ │ ├── use-case/ # contrato UseCase<T, U>
│ │ └── value-objects/ # SingleValueObject, Uuid, Email, Password, Salt, HashedString
│ └── infrastructure/
│ ├── errors/ # filtro HTTP + mapeo status codes
│ ├── event-bus/ # EventBusModule, EventBusNest, EventBusMemory
│ └── hasher/ # HasherBcrypt
├── health-check/ # módulo ejemplo (bounded context)
│ ├── HealthCheckModule.ts
│ ├── domain/
│ │ ├── models/
│ │ └── events/ # EventCheckRequested (con --events)
│ ├── application/
│ │ └── use-cases/ # HealthChecker, EventEmitChecker
│ └── infrastructure/
│ ├── adapters/
│ │ ├── endpoints/
│ │ └── dtos/
│ └── event-handlers/ # EventCheckRequestedHandler (@OnEvent)
└── users/ # bounded context de usuarios (con database)
├── UsersModule.ts
├── domain/
│ ├── models/ # User, UserId, UserName, UserSurname
│ ├── errors/
│ └── repositories/ # puerto UserRepository + token
├── application/
│ └── use-cases/ # UserCreator
└── infrastructure/
└── adapters/
├── endpoints/ # CreateUserEndpoint
└── dtos/TIP
Con create-bb-app, users, EventBus y health de BD solo aparecen si eliges los extras correspondientes. La base siempre incluye el shared kernel y el liveness de health-check.
Flujo del ejemplo health-check
text
HTTP GET /api/v1/health-check
│
▼
HealthCheckEndpoint (infrastructure / adapter)
│
▼
HealthChecker (application / use case)
│
▼
HealthCheckMessage (domain)Flujo de creación de usuario (users)
text
HTTP POST /api/v1/users
│
▼
CreateUserEndpoint (infrastructure / adapter)
│ Email / Password / UserName / UserSurname VOs
▼
UserCreator (application; hasher + UserRepository ports)
│ hasher.generateSalt() + hasher.hash(password, salt)
│ User.createNew(...)
│ userRepository.save(user)
▼
TypeOrmUserRepository / MongoUserRepository
▼
UserDetailsDto (respuesta HTTP; sin password/salt)Regla de oro
domain no importa Nest, TypeORM/Mongoose ni detalles de transporte. Si un caso de uso “necesita” importar infraestructura directamente, falta una interfaz en el dominio.
Direction of dependencies
text
infrastructure → application → domainNunca al revés.
Cómo añadir un feature nuevo
- Crear
src/<feature-kebab>/condomain/,application/use-cases/,infrastructure/adapters/endpoints/. - Añadir
<FeaturePascal>Module.tsregistrando controllers + providers. - Importar el módulo en
ApiModule. - Añadir unit specs junto a use cases / domain.
- Añadir e2e bajo
test/<feature-kebab>.e2e-spec.ts. - Espejar
health-checkcomo referencia.