Agent skill
hexagonal-architecture
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/hexagonal-architecture
SKILL.md
🗄️ Skill: Arquitectura Hexagonal (Backend)
đź“‹ Metadata
| Atributo | Valor |
|---|---|
| ID | backend-hexagonal-architecture |
| Nivel | đź”´ Avanzado |
| VersiĂłn | 1.0.0 |
| Keywords | hexagonal, hexagonal-architecture, ports-and-adapters, ddd, domain-driven-design |
| Referencia | Alistair Cockburn - Hexagonal Architecture |
🔑 Keywords para Invocación
Usa cualquiera de estos keywords en tus prompts para invocar este skill:
hexagonalhexagonal-architectureports-and-adaptersbackend-hexagonal@skill:hexagonal
Ejemplos de Prompts
Crea un microservicio usando arquitectura hexagonal
Implementa un caso de uso siguiendo el patrĂłn ports and adapters
@skill:hexagonal - Estructura el mĂłdulo de usuarios con arquitectura hexagonal en NestJS
đź“– DescripciĂłn
La Arquitectura Hexagonal, tambiĂ©n conocida como Ports and Adapters, es un patrĂłn de diseño que tiene como objetivo crear sistemas desacoplados, donde la lĂłgica de negocio central (el Dominio) estĂ© aislada de las tecnologĂas externas (Bases de datos, Frameworks, APIs externas, UIs).
Esta arquitectura permite que una aplicaciĂłn sea igualmente dirigida por usuarios, programas, tests automatizados o scripts, y sea desarrollada y testead aislada de sus dispositivos de runtime y bases de datos eventuales.
✅ Cuándo Usar Este Skill
- Aplicaciones empresariales complejas.
- Cuando la lógica de negocio es el activo más valioso y debe protegerse de cambios tecnológicos.
- Proyectos que requieren alta testabilidad (Unit Testing sin mocks complejos de frameworks).
- Sistemas que podrĂan cambiar de base de datos o framework en el futuro.
- Microservicios que deben ser mantenibles a largo plazo.
❌ Cuándo NO Usar Este Skill
- Prototipos o MVPs rápidos donde la velocidad de entrega prima sobre la arquitectura.
- Aplicaciones CRUD extremadamente simples.
- Scripts pequeños o herramientas de lĂnea de comandos simples.
🏗️ Estructura del Proyecto (NestJS)
src/
├── modules/
│ └── users/
│ ├── domain/ # 1. Capa de Dominio (El Corazón)
│ │ ├── entities/ # Entidades de dominio (Reglas de negocio)
│ │ ├── value-objects/ # Objetos de valor (Email, Password, etc.)
│ │ ├── repositories/ # INTERFACES de repositorios (Ports de salida)
│ │ ├── services/ # Servicios de dominio (Opcional)
│ │ └── events/ # Eventos de dominio
│ │
│ ├── application/ # 2. Capa de Aplicación (Casos de Uso)
│ │ ├── use-cases/ # Casos de uso (Orquestación)
│ │ ├── dtos/ # Objetos de transferencia de datos (Entrada/Salida)
│ │ ├── mappers/ # Transformadores entre Dominio y DTOs
│ │ └── ports/ # INTERFACES de entrada (Ports de entrada)
│ │
│ ├── infrastructure/ # 3. Capa de Infraestructura (Detalles)
│ │ ├── persistence/ # Implementación de repositorios (TypeORM, Prisma, etc.)
│ │ │ ├── entities/ # Entidades de base de datos (Opcional si usas mappers)
│ │ │ └── repositories/ # Implementación real (Adapters de salida)
│ │ ├── http/ # Controladores o Resolvers (Adapters de entrada)
│ │ │ ├── controllers/
│ │ │ └── resolvers/
│ │ ├── external-services/ # Clientes para APIs externas, SendGrid, etc.
│ │ └── modules/ # NestJS Modules para inyección de dependencias
│ │
│ └── users.module.ts # Punto de entrada de NestJS
đź’» ImplementaciĂłn
1. Capa de Dominio (Sin dependencias externas)
Las entidades de dominio no deben depender de TypeORM, Sequelize o cualquier otro framework.
// src/modules/users/domain/entities/user.entity.ts
export class User {
constructor(
private readonly id: string,
private name: string,
private email: string,
private isActive: boolean,
) {}
public static create(id: string, name: string, email: string): User {
// Validaciones de negocio aquĂ
return new User(id, name, email, true);
}
public deactivate(): void {
this.isActive = false;
}
// Getters
getId(): string { return this.id; }
getName(): string { return this.name; }
getEmail(): string { return this.email; }
getIsActive(): boolean { return this.isActive; }
}
// src/modules/users/domain/repositories/user.repository.interface.ts
// Port de Salida
export interface IUserRepository {
save(user: User): Promise<void>;
findById(id: string): Promise<User | null>;
findByEmail(email: string): Promise<User | null>;
}
2. Capa de AplicaciĂłn (Casos de Uso)
Orquesta el flujo de datos desde y hacia las entidades de dominio.
// src/modules/users/application/use-cases/create-user.use-case.ts
import { Inject, Injectable } from '@nestjs/common';
import { User } from '../../domain/entities/user.entity';
import { IUserRepository } from '../../domain/repositories/user.repository.interface';
import { CreateUserDto } from '../dtos/create-user.dto';
@Injectable()
export class CreateUserUseCase {
constructor(
@Inject('IUserRepository')
private readonly userRepository: IUserRepository,
) {}
async execute(dto: CreateUserDto): Promise<void> {
const existingUser = await this.userRepository.findByEmail(dto.email);
if (existingUser) {
throw new Error('User already exists');
}
const user = User.create(
crypto.randomUUID(),
dto.name,
dto.email
);
await this.userRepository.save(user);
}
}
3. Capa de Infraestructura (Detalles tecnolĂłgicos)
Implementa las interfaces (Adapters) y expone la aplicaciĂłn al exterior.
// src/modules/users/infrastructure/persistence/repositories/typeorm-user.repository.ts
// Adapter de Salida
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from '../../../domain/entities/user.entity';
import { IUserRepository } from '../../../domain/repositories/user.repository.interface';
import { UserEntity } from '../entities/user.entity'; // Entidad de DB
@Injectable()
export class TypeOrmUserRepository implements IUserRepository {
constructor(
@InjectRepository(UserEntity)
private readonly repository: Repository<UserEntity>,
) {}
async save(user: User): Promise<void> {
const dbUser = new UserEntity();
dbUser.id = user.getId();
dbUser.name = user.getName();
dbUser.email = user.getEmail();
dbUser.isActive = user.getIsActive();
await this.repository.save(dbUser);
}
async findByEmail(email: string): Promise<User | null> {
const dbUser = await this.repository.findOneBy({ email });
if (!dbUser) return null;
return new User(dbUser.id, dbUser.name, dbUser.email, dbUser.isActive);
}
// ... rest of methods
}
// src/modules/users/infrastructure/http/controllers/user.controller.ts
// Adapter de Entrada
import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserUseCase } from '../../../application/use-cases/create-user.use-case';
import { CreateUserDto } from '../../../application/dtos/create-user.dto';
@Controller('users')
export class UserController {
constructor(private readonly createUserUseCase: CreateUserUseCase) {}
@Post()
async create(@Body() dto: CreateUserDto) {
return await this.createUserUseCase.execute(dto);
}
}
4. InyecciĂłn de Dependencias (NestJS Module)
Es crucial vincular la interfaz (Port) con la implementaciĂłn (Adapter).
// src/modules/users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CreateUserUseCase } from './application/use-cases/create-user.use-case';
import { UserController } from './infrastructure/http/controllers/user.controller';
import { UserEntity } from './infrastructure/persistence/entities/user.entity';
import { TypeOrmUserRepository } from './infrastructure/persistence/repositories/typeorm-user.repository';
@Module({
imports: [TypeOrmModule.forFeature([UserEntity])],
controllers: [UserController],
providers: [
CreateUserUseCase,
{
provide: 'IUserRepository',
useClass: TypeOrmUserRepository,
},
],
})
export class UsersModule {}
đź§Ş Testing Strategy
La Arquitectura Hexagonal facilita enormemente los tests, especialmente los unitarios de lĂłgica de negocio.
Unit Testing (Domain/Application)
No requieren base de datos, solo mocks de las interfaces.
// test/users/application/create-user.use-case.spec.ts
describe('CreateUserUseCase', () => {
it('should create a user successfully', async () => {
const mockRepo: IUserRepository = {
save: jest.fn(),
findByEmail: jest.fn().mockResolvedValue(null),
findById: jest.fn()
};
const useCase = new CreateUserUseCase(mockRepo);
await useCase.execute({ name: 'John', email: '[email protected]' });
expect(mockRepo.save).toHaveBeenCalled();
});
});
🏆 Mejores Prácticas
- Dependencias hacia adentro: La capa de infraestructura puede conocer a la de aplicaciĂłn y dominio. La de aplicaciĂłn solo al dominio. El dominio no conoce a nadie.
- Sin Miedo a los Mappers: Aunque parezca repetitivo, mapear de
UserEntity(DB) aUser(Dominio) asegura que un cambio en la base de datos no rompa el negocio. - Usa Interfaces para Todo lo Externo: Bases de datos, APIs de terceros, Sistemas de archivos, etc.
- Validaciones en el Dominio: La entidad de dominio debe ser responsable de su propia integridad.
- DIP (Dependency Inversion Principle): Siempre inyecta interfaces, no clases concretas de infraestructura en tus casos de uso.
đź’ˇ Tip: Si usas GraphQL, los Resolvers van en infrastructure/http/resolvers/ de la misma manera que los controladores REST.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?