NestJS

  • Backend
  • typescript
  • node.js
  • [Documentation NestJS - A progressive Node.js framework](https://docs.nestjs.com/)
  • [OpenAPI (Swagger) NestJS - A progressive Node.js framework](https://docs.nestjs.com/openapi/introduction)

Introducción general

  • Framework progresivo para Node.js basado en TypeScript, inspirado en Angular.
  • Promueve mantenibilidad, modularidad, testabilidad y una arquitectura limpia.
  • Construido sobre Express (por defecto) o Fastify (para mejor rendimiento).
  • Enfoque basado en decoradores y metadatos, aprovechando la inyección de dependencias.

Elementos principales

  • Controllers → manejan rutas y peticiones HTTP.
  • Services (Providers) → encapsulan la lógica de negocio.
  • Entities y DTOs → estructuras de datos para persistencia y transporte.
  • Middleware → lógica intermedia en el flujo de petición.
  • Pipes → validación y transformación de datos.
  • Guards → control de acceso y autorización.
  • Interceptors → manipulación de respuesta o comportamiento en runtime.
  • Decoradores personalizados → metaprogramación reutilizable.
  • CLI → automatiza creación de módulos, controladores, servicios y recursos.
  • Testing → integración nativa con Jest.

Recursos introductorios

  • ¿Qué es NESTJS? 🤔 Nest JS vs Express Parte 1 - YouTube
    • Controller: usa decoradores (@Controller, @Get, @Post) y define rutas.
    • Service: se inyecta mediante el constructor (constructor(private readonly service: ...)).
    • Base de datos: se modela y conecta desde los services.
    • Postman: usado para comprobar endpoints.
    • CLI Resource:
      • Comando: nest generate resource <nombre>
      • Crea automáticamente clases, métodos y respuestas.
      • Permite acceso directo a rutas pasando strings en el controller.
    • Swagger: integración directa para documentación.
      • OpenAPI (Swagger) - NestJS - A progressive Node.js framework
      • Decorador @ApiTags organiza los endpoints.
      • Permite crear API tipadas.

NestJS + Next.js + Prisma (Fullstack moderno)

  • Nextjs y Nestjs - Aplicación CRUD (con Typescript, Shadcn, prisma y más) - YouTube
  • [Get started with Prisma Prisma Documentation](https://www.prisma.io/docs/getting-started)
    • NestJS: backend API (REST o GraphQL).
    • Next.js: frontend React SSR/SSG.
    • Prisma ORM: acceso tipado a base de datos.
    • CRUD completo (Create, Read, Update, Delete).
    • Integración con Shadcn/UI.
    • Ideal para proyectos con frontend moderno y backend modular.

Estructura de proyectos


Sistema de pagos con Arquitectura Hexagonal (Clean Architecture)

Arquitectura Hexagonal

  • Divide el sistema en Dominios, Aplicación e Infraestructura.
  • Las capas internas no dependen de las externas.
  • Comunicación mediante Puertos (Interfaces) y Adaptadores (Implementaciones).
  • Uso del patrón Unit of Work para operaciones atómicas (por ejemplo, decoradores para transacciones en MongoDB o PostgreSQL).
    • Decorador de transacción (ejemplo implementado en Middleware) 👆

Dominio (Domain Layer)

  • Carpeta: context/payments/domain
  • Entidad payment.ts
    • Define primitivos (ID, cliente, cantidad) con interfaces.
    • Construcción de la entidad usando atributos tipados.
    • Métodos:
      • static createPayment()
      • toPrimitives() → convierte el objeto a datos simples.
    • Uso de uuid para IDs únicos.
    • Ejemplo de creación de pago:
const payment = Payment.create({
	id: uuid(),
	customerId: '123',
	amount: 100
});

Clases abstractas y repositorios

  • Archivo: payment.repository.ts
	export abstract class PaymentRepository {
		abstract save(payment: Payment): Promise<void>;
		abstract findById(id: string): Promise<Payment | null>;
	}
	```

- **Excepciones personalizadas:**
	
```ts
	export class PaymentNotFoundException extends Error {
		constructor(id: string) {
			super(`Payment with ID ${id} not found`);
		}
	}
	```


---

## Casos de uso (Application Layer)
- Ejemplo: `create-payment.usecase.ts`
	
```ts
	export interface CreatePaymentDto {
		id: string;
		customerId: string;
		amount: number;
	}
	```


	
```ts
	export class CreatePaymentUseCase {
		constructor(
			private readonly repository: PaymentRepository
		) {}

		async execute(dto: CreatePaymentDto): Promise<PaymentPrimitives> {
			const payment = Payment.create(dto);
			await this.repository.save(payment);
			return payment.toPrimitives();
		}
	}
	```

- El caso de uso construye el repositorio con métodos abstractos.
- Devuelve objetos compuestos únicamente por primitivos.
- Cada caso de uso orquesta la lógica entre dominio e infraestructura.

---

## Infraestructura (Infra Layer)
- Compuesta por módulos, controladores y servicios inyectables.
- **DTOs** para transporte de datos HTTP:
	- `payment_http-dto.ts`
- Ejemplo básico:
	
```ts
	@Controller('payments')
	export class PaymentController {
		constructor(private readonly createPayment: CreatePaymentUseCase) {}

		@Post()
		async create(@Body() dto: CreatePaymentDto) {
			return this.createPayment.execute(dto);
		}
	}
	```

- **Capa agnóstica de la base de datos**.
- **Carpeta shared:** código y utilidades comunes.
- Configuración mediante `@Module()` con `providers`.
- Implementación de repositorios concretos que cumplen las interfaces del dominio.

### Pendientes / TODOs
- Endpoint de creación y listado de pagos.
- Injectable personalizado para casos de uso.
- Verificación de endpoints en Postman.
- Manejo de excepciones HTTP (`NotFoundException`, `BadRequestException`, etc.)
- Separación entre **DTOs de capa HTTP** y **DTOs de aplicación**.
- Repositorio lanza excepción si no existe el pago.

---

# NestJS – Avanzado y Ecosistema

- NestJS
- [Backend](/backend/backend/)
- [typescript](/software%20engineering/typescript/)

---

## Core avanzado y ejecución interna
- NestJS está construido sobre un **módulo de inyección de dependencias (IoC Container)**.
	- Permite gestionar el **ciclo de vida** de instancias y sus dependencias.
	- Usa **reflexión** (metadata) y el decorador `@Injectable()` para registrar providers.
- **Módulos dinámicos**
	- Permiten registrar dependencias de forma condicional o configurable.
	- Ejemplo: `ConfigModule.forRoot({ isGlobal: true })`
	- Útiles para configurar autenticación, conexión a DB o servicios externos.
- **Scopes de providers**
	- `Scope.DEFAULT` → singleton.
	- `Scope.REQUEST` → nueva instancia por request.
	- `Scope.TRANSIENT` → nueva instancia por cada inyección.

---

## Inyección de dependencias avanzada
- **Custom Providers:**
	- Se puede inyectar usando `useClass`, `useExisting`, `useValue`, `useFactory`.
	- Ejemplo:
		
```ts
		@Module({
			providers: [
				{
					provide: 'PAYMENT_REPOSITORY',
					useClass: PostgresPaymentRepository,
				},
			],
		})
		```

- **Circular dependencies:** 
	- Resolver con `forwardRef(() => ServiceA)` cuando dos módulos dependen entre sí.
- **Value providers:** útil para constantes globales o tokens de configuración.

---

## Middleware, Guards, Pipes e Interceptors
### Middleware
- Ejecutan antes de llegar a los controladores.
- Implementan `NestMiddleware` y se aplican con `app.use()` o en el módulo.
	
```ts
	app.use(loggerMiddleware);
	```


### Guards
- Controlan acceso y autorización.
- Retornan `true` o `false` según permisos o roles.
	
```ts
	@UseGuards(AuthGuard)
	@Get('profile')
	getProfile() {}
	```


### Pipes
- Validan y transforman los datos entrantes.
	- `ValidationPipe`, `ParseIntPipe`, o pipes personalizados.
	- Se aplican globalmente: 
		
```ts
		app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
		```


### Interceptors
- Modifican las respuestas, loguean o miden tiempos.
	- Ejemplo: LoggingInterceptor, TransformInterceptor.
	
```ts
	@UseInterceptors(LoggingInterceptor)
	@Get()
	findAll() {}
	```


---

## Microservicios y comunicación distribuida
- NestJS permite crear **microservicios nativos** mediante el módulo `@nestjs/microservices`.
- Soporta distintos **transportes**:
	- `TCP`, `Redis`, `MQTT`, `NATS`, `Kafka`, `gRPC`.
- Ejemplo de microservicio con **Redis transport**:
	
```ts
	const app = await NestFactory.createMicroservice(AppModule, {
		transport: Transport.REDIS,
		options: { url: 'redis://localhost:6379' },
	});
	app.listen();
	```

- Uso de `@MessagePattern()` en lugar de `@Controller()` para procesar eventos.
- Integración natural con patrones **CQRS** y **Event Sourcing**.

---

## CQRS y Event Sourcing
- **CQRS (Command Query Responsibility Segregation)** separa operaciones de lectura y escritura.
- Compatible con `@nestjs/cqrs`.
	- **Commands:** ejecutan acciones que cambian estado.
	- **Queries:** obtienen datos sin mutarlos.
	- **Events:** notifican cambios.
- Ejemplo:
	
```ts
	export class CreatePaymentCommand {
		constructor(public readonly dto: CreatePaymentDto) {}
	}
	```

	
```ts
	@CommandHandler(CreatePaymentCommand)
	export class CreatePaymentHandler {
		constructor(private readonly repo: PaymentRepository) {}

		async execute(command: CreatePaymentCommand) {
			const payment = Payment.create(command.dto);
			await this.repo.save(payment);
		}
	}
	```

- El patrón **Event Sourcing** registra los eventos del dominio en lugar del estado final, ideal para auditoría y resiliencia.

---

## GraphQL en NestJS
- Soporte nativo mediante `@nestjs/graphql`.
- Basado en `apollo-server` o `Mercurius` (para Fastify).
- Tipado automático a partir de DTOs con decoradores:
	
```ts
	@Resolver(() => User)
	export class UserResolver {
		constructor(private userService: UserService) {}

		@Query(() => [User])
		async users() {
			return this.userService.findAll();
		}
	}
	```

- Modos:
	- **Code First:** esquemas generados desde clases TypeScript.
	- **Schema First:** esquemas definidos en archivos `.graphql`.

---

## WebSockets y Gateway en tiempo real
- Basado en `@WebSocketGateway` y `@SubscribeMessage`.
- Compatible con `socket.io` y `ws`.
	
```ts
	@WebSocketGateway()
	export class ChatGateway {
		@SubscribeMessage('message')
		handleMessage(client: Socket, payload: string): void {
			client.broadcast.emit('message', payload);
		}
	}
	```

- Ideal para chats, notificaciones, juegos o sistemas colaborativos.

---

## Testing en NestJS
- Integración directa con **Jest**.
- Tipos de tests:
	- **Unit tests**  testean servicios o lógica de negocio aislada.
	- **e2e tests**  prueban módulos completos usando `@nestjs/testing`.
- Ejemplo de test unitario:
	
```ts
	describe('PaymentsService', () => {
		let service: PaymentsService;

		beforeEach(async () => {
			const module = await Test.createTestingModule({
				providers: [PaymentsService],
			}).compile();
			service = module.get<PaymentsService>(PaymentsService);
		});

		it('should create a payment', () => {
			expect(service.create({ id: '1', amount: 10 })).toBeDefined();
		});
	});
	```

- Integrar **coverage reports**, **mocking de repositorios**, y **TestContainers** para bases de datos reales en CI/CD.

---

## Optimización y despliegue
- **Compilación:** usar SWC o esbuild para tiempos de build menores.
- **Monitorización:** integrar con Prometheus o OpenTelemetry.
- **Despliegue:**
	- [Kubernetes](/virtualizacion/kubernetes/) con `Helm` o `kustomize`.
	- Escalabilidad horizontal con microservicios y gateways.
	- Integrar pipelines CI/CD para lint, test y build automáticos.

---

## Ecosistema y extensiones útiles
- **NestJS CLI Plugins:**
	- `@nestjs/config`  variables de entorno.
	- `@nestjs/swagger`  documentación automática.
	- `@nestjs/passport`  autenticación JWT, OAuth.
	- `@nestjs/terminus`  health checks.
	- `@nestjs/schedule`  cron jobs.
- **Integraciones recomendadas:**
	- Prisma o TypeORM (ORMs)
	- Redis (cache, pub/sub)
	- [RabbitMQ](/backend/rabbitmq/) o Kafka (mensajería)
	- [OpenTelemetry](/monitoreo/opentelemetry/) (monitorización)
- **Plantillas de referencia:**
	- [nestjs-prisma-starter](https://github.com/notiz-dev/nestjs-prisma-starter)
	- [nestjs-boilerplate](https://github.com/samchon/nestia)

---

## Buenas prácticas y principios
- Mantener cada **módulo con alta cohesión y bajo acoplamiento**.
- Evitar lógica de negocio en controladores.
- Usar **DTOs** para validar toda entrada externa.
- Desacoplar infraestructura (ORMs, HTTP) del dominio.
- Añadir **decoradores personalizados** para validaciones o logging.
- Modularizar según **bounded contexts** (DDD).
- Monitorizar tiempos con **Interceptors** y logs estructurados.
- Mantener configuración centralizada con `ConfigService`.

---

## Relacionado
- Arquitectura Hexagonal
- CQRS y Event Sourcing
- Microservicios
- Backend Testing
- [GraphQL](/backend/graphql/)
- [Kubernetes](/virtualizacion/kubernetes/)
# NestJS  Arquitectura Aplicada con Patrones Avanzados

- NestJS
- Arquitectura Hexagonal
- CQRS y Event Sourcing
- DDD
- Backend Patterns

---

## Enfoque General
Esta arquitectura integra **principios de DDD, CQRS y Event Sourcing** sobre un stack moderno con **Prisma**, **Swagger**, **Auth modular**, y **observabilidad distribuida**.  
El objetivo es mantener **alta escalabilidad, trazabilidad y mantenibilidad** en sistemas complejos o de misión crítica.

---

## 1. Estructura Modular
### Capas Principales
- **Domain:** entidades, value objects, agregados y eventos del dominio.  
- **Application:** casos de uso (commands, queries, handlers).  
- **Infrastructure:** persistencia, adaptadores externos, Prisma, colas, APIs.  
- **Interface:** controladores, resolvers, DTOs, validaciones y Swagger.

### Ejemplo de estructura:

src/ ├── domain/ │ ├── user.entity.ts │ ├── events/ │ └── value-objects/ ├── application/ │ ├── commands/ │ ├── queries/ │ ├── handlers/ │ └── services/ ├── infrastructure/ │ ├── prisma/ │ ├── repositories/ │ └── adapters/ └── interface/ ├── controllers/ ├── graphql/ └── dto/

`

---

## 2. Integración de CQRS + Event Sourcing
- Se utiliza `@nestjs/cqrs` para separar lectura/escritura y capturar eventos.
- **CommandHandlers** ejecutan acciones de negocio.
- **EventHandlers** persisten o publican eventos.
- **EventStore** centraliza los eventos (por ejemplo, con PostgreSQL o Kafka).

### Ejemplo de Command + Event

```ts
export class CreateUserCommand {
	constructor(public readonly dto: CreateUserDto) {}
}

@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
	constructor(
		private readonly repo: UserRepository,
		private readonly eventBus: EventBus
	) {}

	async execute(command: CreateUserCommand) {
		const user = User.create(command.dto);
		await this.repo.save(user);
		this.eventBus.publish(new UserCreatedEvent(user.id));
	}
}

`

Event Handler

@EventsHandler(UserCreatedEvent)
export class UserCreatedHandler implements IEventHandler<UserCreatedEvent> {
	handle(event: UserCreatedEvent) {
		console.log('User created:', event.userId);
	}
}

3. Persistencia con Prisma

  • Prisma se usa como capa de infraestructura, desacoplada del dominio.
  • Los repositorios implementan interfaces de dominio (IUserRepository) para facilitar testeo y mocks.
@Injectable()
export class PrismaUserRepository implements IUserRepository {
	constructor(private prisma: PrismaService) {}

	async save(user: User): Promise<void> {
		await this.prisma.user.create({ data: user.toPrimitives() });
	}

	async findById(id: string): Promise<User | null> {
		const data = await this.prisma.user.findUnique({ where: { id } });
		return data ? User.fromPrimitives(data) : null;
	}
}

4. Documentación con Swagger

  • Integrar desde main.ts:
  const config = new DocumentBuilder()
  	.setTitle('Advanced API')
  	.setDescription('API with CQRS + Prisma + Auth + Observability')
  	.setVersion('1.0')
  	.addBearerAuth()
  	.build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api/docs', app, document);
  • Genera documentación interactiva y permite probar endpoints autenticados.
  • Combinar con decoradores @ApiTags, @ApiBearerAuth, @ApiResponse.

5. Autenticación y Autorización Avanzadas

  • Uso de JWT + Passport para autenticación modular.
  • AuthModule define estrategias (JwtStrategy, LocalStrategy).
  • Guards controlan acceso según roles o claims:
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Get('me')
  @ApiBearerAuth()
  getProfile(@Request() req) {
  	return req.user;
  }
  • Integrar RBAC (Role-Based Access Control) o ABAC (Attribute-Based Access Control) mediante decorators personalizados.

6. Observabilidad, Logging y Tracing

  • Integración con OpenTelemetry y Prometheus.
  • Cada request puede generar un trace distribuido con traceId.
  • Configuración típica:
  import { OpenTelemetryModule } from 'nestjs-otel';

  @Module({
  	imports: [OpenTelemetryModule.forRoot()],
  })
  export class ObservabilityModule {}
  • Métricas expuestas en /metrics para Prometheus.
  • Logging estructurado con nestjs-pino o winston:
  app.useLogger(app.get(Logger));

7. Patrones Compuestos

Saga + EventBus + Repository

  • Saga coordina varios pasos distribuidos.

    • Escucha eventos y dispara comandos según el flujo.
  @Saga()
  export class PaymentSaga {
  	userCreated = (events$: Observable<UserCreatedEvent>) =>
  		events$.pipe(
  			map(event => new InitializePaymentCommand(event.userId))
  		);
  }

Factory + Strategy dentro del dominio

  • Factory crea entidades o agregados coherentes.
  • Strategy aplica diferentes políticas de negocio (p.e. cálculo de comisiones).
  export interface PaymentStrategy {
  	calculate(amount: number): number;
  }
  export class PremiumPaymentStrategy implements PaymentStrategy {
  	calculate(amount: number) {
  		return amount * 0.9;
  	}
  }

8. Testing de Arquitectura Completa

  • Unit testing con jest.mock() para repositorios.
  • Integration testing con Test.createTestingModule.
  • e2e testing simulando el flujo CQRS completo con supertest.

Ejemplo e2e:

describe('User lifecycle (e2e)', () => {
	let app: INestApplication;

	beforeAll(async () => {
		const moduleFixture = await Test.createTestingModule({
			imports: [AppModule],
		}).compile();

		app = moduleFixture.createNestApplication();
		await app.init();
	});

	it('/POST user', async () => {
		return request(app.getHttpServer())
			.post('/users')
			.send({ name: 'Alice', email: 'alice@mail.com' })
			.expect(201);
	});
});

9. CI/CD y despliegue

  • Lint + Test + Build + Deploy en pipelines automáticos.
  • Versionado semántico y publicación con nx release o semantic-release.
  • Integración con Docker y Kubernetes.
  • Variables seguras gestionadas por Vault o AWS Secrets Manager.
  • Despliegue multi-entorno (dev/staging/prod) con configuración por módulo dinámico.

10. Beneficios y conclusiones

  • Escalabilidad horizontal gracias a CQRS + Microservicios.
  • Trazabilidad completa mediante Event Sourcing + Observabilidad.
  • Consistencia y testabilidad por repositorios tipados e interfaces claras.
  • Adaptable a cualquier dominio con mínima fricción técnica.

Relacionado

  • NestJS – Avanzado y Ecosistema
  • Arquitectura Hexagonal
  • CQRS y Event Sourcing
  • DDD
  • Observabilidad
  • Auth y Seguridad Backend

NestJS – Caso Práctico Completo: User + Payment Service

  • NestJS
  • CQRS y Event Sourcing
  • Arquitectura Hexagonal
  • Prisma
  • Auth y Seguridad Backend
  • Observabilidad
  • DDD

🌐 Visión General del Microservicio

Este ejemplo muestra un microservicio realista que maneja usuarios y pagos.
Combina:

  • CQRS (Command/Query separation)
  • Prisma (persistencia desacoplada)
  • EventBus (propagación de eventos)
  • JWT Auth (seguridad modular)
  • Observabilidad distribuida

El objetivo es ilustrar cómo aplicar arquitectura hexagonal + patrones DDD de forma mantenible y escalable.


🧩 Estructura de Carpetas


src/
├── domain/
│	├── user/
│	│	├── user.entity.ts
│	│	└── events/
│	├── payment/
│	│	├── payment.entity.ts
│	│	└── events/
│	└── shared/
│		└── value-objects/
│
├── application/
│	├── user/
│	│	├── commands/
│	│	├── queries/
│	│	└── handlers/
│	├── payment/
│	│	├── commands/
│	│	├── queries/
│	│	└── handlers/
│	└── common/
│		└── interfaces/
│
├── infrastructure/
│	├── prisma/
│	│	└── prisma.service.ts
│	├── repositories/
│	│	├── user.repository.ts
│	│	└── payment.repository.ts
│	└── event-bus/
│		└── event.module.ts
│
└── interface/
├── controllers/
│	├── user.controller.ts
│	└── payment.controller.ts
├── dto/
└── auth/
├── jwt.strategy.ts
└── auth.guard.ts

`


🧠 Dominio

Entidad User

export class User {
	constructor(
		private readonly id: string,
		private readonly email: string,
		private readonly balance: number = 0
	) {}

	static create(email: string): User {
		const id = crypto.randomUUID();
		return new User(id, email, 0);
	}

	increaseBalance(amount: number): User {
		return new User(this.id, this.email, this.balance + amount);
	}

	toPrimitives() {
		return { id: this.id, email: this.email, balance: this.balance };
	}
}

`

Entidad Payment

export class Payment {
	constructor(
		private readonly id: string,
		private readonly userId: string,
		private readonly amount: number,
		private readonly timestamp: Date
	) {}

	static create(userId: string, amount: number): Payment {
		return new Payment(crypto.randomUUID(), userId, amount, new Date());
	}

	toPrimitives() {
		return {
			id: this.id,
			userId: this.userId,
			amount: this.amount,
			timestamp: this.timestamp,
		};
	}
}

⚙️ Aplicación (CQRS)

Command: CreatePaymentCommand

export class CreatePaymentCommand {
	constructor(
		public readonly userId: string,
		public readonly amount: number
	) {}
}

CommandHandler

@CommandHandler(CreatePaymentCommand)
export class CreatePaymentHandler implements ICommandHandler<CreatePaymentCommand> {
	constructor(
		private readonly paymentRepo: PaymentRepository,
		private readonly userRepo: UserRepository,
		private readonly eventBus: EventBus
	) {}

	async execute(command: CreatePaymentCommand): Promise<void> {
		const user = await this.userRepo.findById(command.userId);
		if (!user) throw new Error('User not found');

		const payment = Payment.create(user.id, command.amount);
		await this.paymentRepo.save(payment);

		const updated = user.increaseBalance(command.amount);
		await this.userRepo.save(updated);

		this.eventBus.publish(new PaymentCreatedEvent(payment.id, user.id));
	}
}

Event

export class PaymentCreatedEvent {
	constructor(
		public readonly paymentId: string,
		public readonly userId: string
	) {}
}

🧱 Infraestructura

Prisma Repository (implementa interfaces)

@Injectable()
export class PrismaUserRepository implements UserRepository {
	constructor(private prisma: PrismaService) {}

	async findById(id: string): Promise<User | null> {
		const data = await this.prisma.user.findUnique({ where: { id } });
		return data ? new User(data.id, data.email, data.balance) : null;
	}

	async save(user: User): Promise<void> {
		await this.prisma.user.upsert({
			where: { id: user.toPrimitives().id },
			create: user.toPrimitives(),
			update: user.toPrimitives(),
		});
	}
}

EventBus (in-memory o Kafka)

@Module({
	imports: [CqrsModule],
	providers: [],
	exports: [CqrsModule],
})
export class EventBusModule {}

🔒 Autenticación (JWT)

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
	constructor() {
		super({
			jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
			ignoreExpiration: false,
			secretOrKey: process.env.JWT_SECRET,
		});
	}

	async validate(payload: any) {
		return { userId: payload.sub, email: payload.email };
	}
}

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

🌍 Interfaz (HTTP Layer)

Controlador de Users

@ApiTags('users')
@Controller('users')
export class UserController {
	constructor(private readonly userRepo: UserRepository) {}

	@Post()
	async create(@Body() dto: { email: string }) {
		const user = User.create(dto.email);
		await this.userRepo.save(user);
		return user.toPrimitives();
	}

	@Get(':id')
	async getById(@Param('id') id: string) {
		const user = await this.userRepo.findById(id);
		return user ? user.toPrimitives() : { error: 'Not found' };
	}
}

Controlador de Payments

@ApiTags('payments')
@Controller('payments')
@UseGuards(JwtAuthGuard)
export class PaymentController {
	constructor(private commandBus: CommandBus) {}

	@Post()
	async createPayment(@Body() dto: { userId: string; amount: number }) {
		await this.commandBus.execute(new CreatePaymentCommand(dto.userId, dto.amount));
		return { message: 'Payment processed' };
	}
}

📊 Observabilidad

Integración con OpenTelemetry:

@Module({
	imports: [
		OpenTelemetryModule.forRoot({
			metrics: { hostMetrics: true, apiMetrics: { enable: true } },
		}),
	],
})
export class ObservabilityModule {}
  • Logs con nestjs-pino:
  app.useLogger(app.get(Logger));
  • Endpoint /metrics para Prometheus.

🧪 Testing de Flujo Completo

Prueba e2e del ciclo User → Payment

describe('User + Payment flow (e2e)', () => {
	let app: INestApplication;

	beforeAll(async () => {
		const moduleRef = await Test.createTestingModule({
			imports: [AppModule],
		}).compile();

		app = moduleRef.createNestApplication();
		await app.init();
	});

	it('should create a user and process payment', async () => {
		const user = await request(app.getHttpServer())
			.post('/users')
			.send({ email: 'test@mail.com' })
			.expect(201);

		await request(app.getHttpServer())
			.post('/payments')
			.send({ userId: user.body.id, amount: 100 })
			.set('Authorization', `Bearer ${process.env.JWT}`)
			.expect(201);
	});
});

🚀 Beneficios del Caso

  • Dominio agnóstico de la infraestructura.
  • Separación clara CQRS: Commands → cambios, Queries → lectura.
  • Escalabilidad horizontal: microservicios desacoplados.
  • EventBus extensible: posible integración con Kafka/NATS.
  • Testing y trazabilidad completas.

📚 Referencias Relacionadas

  • NestJS – Arquitectura Aplicada con Patrones Avanzados
  • CQRS y Event Sourcing
  • Arquitectura Hexagonal
  • Auth y Seguridad Backend
  • Observabilidad
  • Prisma
  • DDD

Omnivore NestJS

type: list
name: "Notas con #nestjs en Omnivore"
order:
  - property: date_saved
    direction: desc
columns:
  - file.name
  - date_saved
filters:
  and:
    - file.inFolder("Omnivore")
    - file.hasTag("nestjs", "Nestjs")
views:
  - type: table
    name: Table
    sort:
      - property: file.mtime
        direction: DESC

`