Skip to content

Repository files navigation

CampaignFlowAPI

Clean architecture backend built with Fastify, TypeScript, and Prisma. Synchronizes Meta Ads mock accounts, campaigns, and metrics with PostgreSQL, implementing hexagonal architecture and concurrency-safe sync flows.

Features

  • Clean Architecture: Hexagonal architecture with clear separation of concerns
  • Fastify: High-performance web framework
  • TypeScript: Full type safety
  • Prisma: Type-safe database ORM
  • PostgreSQL: Relational database
  • Swagger/OpenAPI: Auto-generated API documentation
  • Concurrency-Safe: Safe concurrent sync operations using Promise.allSettled

Project Structure

src/
β”œβ”€β”€ api/                    # API layer (controllers, routes, schemas)
β”œβ”€β”€ application/            # Application layer (use cases, services)
β”œβ”€β”€ domain/                 # Domain layer (entities, repositories interfaces, value objects)
└── infrastructure/         # Infrastructure layer (Prisma, HTTP clients, config)

Prerequisites

  • Node.js v22 or higher
  • PostgreSQL database (v14 or higher)
  • npm or yarn
  • Docker and Docker Compose (optional, for containerized deployment)

πŸš€ Setup Local

Paso a Paso para Ejecutar Localmente

1. Clonar el Repositorio

git clone <repository-url>
cd CampaignFlowAPI

2. Instalar Dependencias

npm install

3. Configurar Variables de Entorno

Crea un archivo .env en la raΓ­z del proyecto con las siguientes variables:

# Database Configuration
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/meta_backend"
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=meta_backend
POSTGRES_PORT=5432

# Application Configuration
PORT=3000
NODE_ENV=development

# External API Configuration
META_API_BASE_URL=https://w5k577bkq5cmihbdxxqlok2c7y0ejbiz.lambda-url.us-east-1.on.aws
META_API_TIMEOUT=30000
META_API_RETRY_ATTEMPTS=3

# Sync Configuration
SYNC_ALL_TIMEOUT=300000

Nota: Si usas Docker Compose, las credenciales por defecto son postgres:postgres.

4. Iniciar PostgreSQL

OpciΓ³n A: Usando Docker (Recomendado)

# Iniciar solo PostgreSQL
docker-compose up -d postgres

# Verificar que estΓ‘ corriendo
docker-compose ps

# Esperar unos segundos para que PostgreSQL estΓ© listo

OpciΓ³n B: PostgreSQL Local

AsegΓΊrate de tener PostgreSQL v14+ instalado y corriendo localmente, luego actualiza DATABASE_URL en .env con tus credenciales.

5. Configurar Base de Datos

# Generar Prisma Client
npm run prisma:generate

# Ejecutar migraciones (crea las tablas)
npm run prisma:migrate

# Poblar la base de datos con datos iniciales (seed)
npm run prisma:seed

Nota: Cuando ejecutes prisma:migrate, se te pedirΓ‘ un nombre para la migraciΓ³n. Puedes usar init o initial_schema.

Si hay errores de conexiΓ³n, verifica:

  • PostgreSQL estΓ‘ corriendo: docker-compose ps (si usas Docker)
  • DATABASE_URL en .env coincide con tus credenciales
  • La base de datos meta_backend existe (Prisma la crea automΓ‘ticamente si no existe)

6. Iniciar Servidor de Desarrollo

npm run dev

El servidor estarΓ‘ disponible en http://localhost:3000

7. Verificar que Funciona

# Health check
curl http://localhost:3000/health

# DeberΓ­as recibir:
# {
#   "status": "ok",
#   "timestamp": "...",
#   "database": { "status": "connected" }
# }

8. Acceder a DocumentaciΓ³n

Abre tu navegador en: http://localhost:3000/docs

Swagger UI te permitirΓ‘ probar todos los endpoints interactivamente.

9. Visualizar y Gestionar la Base de Datos

βœ… ConfirmaciΓ³n: Este proyecto usa Prisma ORM

El schema de Prisma estΓ‘ en: src/infrastructure/database/prisma/schema.prisma

OpciΓ³n 1: Prisma Studio (Recomendado - GUI Visual)

Prisma Studio es una interfaz grΓ‘fica oficial de Prisma que te permite ver y editar datos directamente.

# Iniciar Prisma Studio
npm run prisma:studio

# Esto abrirΓ‘ automΓ‘ticamente en tu navegador:
# http://localhost:5555

CaracterΓ­sticas:

  • βœ… Interfaz visual moderna
  • βœ… Ver todas las tablas (Account, Campaign)
  • βœ… Editar datos directamente
  • βœ… Filtrar y buscar registros
  • βœ… Crear nuevos registros
  • βœ… Ver relaciones entre tablas
OpciΓ³n 2: psql (LΓ­nea de Comandos)
# Conectar a la base de datos usando psql
# Windows (si tienes PostgreSQL instalado):
psql -U postgres -d meta_backend -h localhost -p 5432

# O desde Docker:
docker exec -it campaignflow-postgres psql -U postgres -d meta_backend

# Comandos ΓΊtiles:
\dt                    # Listar todas las tablas
\d Account             # Ver estructura de la tabla Account
\d Campaign            # Ver estructura de la tabla Campaign
SELECT * FROM "Account";    # Ver todos los registros de Account
SELECT * FROM "Campaign";   # Ver todos los registros de Campaign
\q                     # Salir
OpciΓ³n 3: pgAdmin (GUI Completa)

pgAdmin es una herramienta grΓ‘fica completa para PostgreSQL.

  1. Descarga desde: https://www.pgadmin.org/download/
  2. Instala pgAdmin 4
  3. Crea una nueva conexiΓ³n:
    • Host: localhost
    • Port: 5432
    • Database: meta_backend
    • Username: postgres
    • Password: postgres
OpciΓ³n 4: DBeaver (Multi-Database GUI)

DBeaver es un cliente SQL universal que funciona con mΓΊltiples bases de datos.

  1. Descarga desde: https://dbeaver.io/download/
  2. Instala DBeaver Community Edition
  3. Crea una nueva conexiΓ³n PostgreSQL:
    • Host: localhost
    • Port: 5432
    • Database: meta_backend
    • Username: postgres
    • Password: postgres
OpciΓ³n 5: TablePlus (GUI Moderna - macOS/Windows)

TablePlus es una herramienta moderna y elegante para bases de datos.

  1. Descarga desde: https://tableplus.com/
  2. Instala TablePlus
  3. Crea una nueva conexiΓ³n PostgreSQL con las mismas credenciales
Estructura de la Base de Datos

Tabla: Account

  • id: String (UUID o acct1-acct5)
  • name: String (max 255 caracteres)
  • createdAt: DateTime
  • updatedAt: DateTime
  • RelaciΓ³n: Un Account tiene muchas Campaigns (1:N)

Tabla: Campaign

  • id: String (UUID)
  • name: String (max 255 caracteres)
  • status: CampaignStatus (ACTIVE, PAUSED, DELETED, ARCHIVED)
  • spend: Decimal(10,2) - Gasto actual
  • budget: Decimal(10,2) - Presupuesto
  • accountId: String (Foreign Key a Account)
  • createdAt: DateTime
  • updatedAt: DateTime
  • Índices: accountId, status
Queries Útiles
-- Ver todas las cuentas
SELECT * FROM "Account" ORDER BY "createdAt" DESC;

-- Ver todas las campaΓ±as con su cuenta
SELECT 
  c.id,
  c.name,
  c.status,
  c.spend,
  c.budget,
  a.name AS account_name
FROM "Campaign" c
JOIN "Account" a ON c."accountId" = a.id
ORDER BY c."createdAt" DESC;

-- Contar campaΓ±as por cuenta
SELECT 
  a.id,
  a.name,
  COUNT(c.id) AS total_campaigns,
  SUM(c.spend) AS total_spend,
  SUM(c.budget) AS total_budget
FROM "Account" a
LEFT JOIN "Campaign" c ON a.id = c."accountId"
GROUP BY a.id, a.name;

-- Ver campaΓ±as activas
SELECT * FROM "Campaign" 
WHERE status = 'ACTIVE' 
ORDER BY spend DESC;
Comandos Prisma Útiles
# Generar Prisma Client (despuΓ©s de cambiar schema)
npm run prisma:generate

# Crear nueva migraciΓ³n (despuΓ©s de cambiar schema)
npm run prisma:migrate

# Aplicar migraciones en producciΓ³n
npm run prisma:migrate:deploy

# Abrir Prisma Studio (GUI visual)
npm run prisma:studio

# Ejecutar seed (poblar datos iniciales)
npm run prisma:seed

# Ver formato del schema
npx prisma format --schema=./src/infrastructure/database/prisma/schema.prisma

# Validar schema
npx prisma validate --schema=./src/infrastructure/database/prisma/schema.prisma

Para mΓ‘s detalles, consulta la guΓ­a completa en GUIA_DATABASE.md

Production Build

To build and run in production mode:

# Build the project
npm run build

# Start production server
npm run start

API Documentation

Once the server is running, access the Swagger documentation at:

http://localhost:3000/docs

Available Endpoints

Accounts

  • GET /accounts - Get all accounts
  • POST /accounts - Create a new account
  • POST /accounts/sync - Sync accounts from external Meta Ads API

Campaigns

  • POST /accounts/:id/campaigns/sync - Sync campaigns for a specific account
  • GET /accounts/:id/campaigns/metrics - Get campaign metrics for an account
  • POST /sync/all - Sync all campaigns for all accounts concurrently

Health

  • GET /health - Health check endpoint

Scripts

  • npm run dev - Start development server with hot reload
  • npm run build - Build the project
  • npm run start - Start production server
  • npm run test - Run all tests
  • npm run test:unit - Run unit tests only
  • npm run test:integration - Run integration tests only
  • npm run prisma:generate - Generate Prisma client
  • npm run prisma:migrate - Run database migrations
  • npm run prisma:migrate:deploy - Deploy migrations in production
  • npm run prisma:studio - Open Prisma Studio (database GUI)
  • npm run prisma:seed - Seed database with initial data

Testing

The project uses Vitest for testing. Tests are organized in:

  • tests/unit/ - Unit tests for use cases and services
  • tests/integration/ - Integration tests for API endpoints
  • tests/mocks/ - Mock utilities for testing

Run tests with:

# Run all tests
npm test

# Run unit tests only
npm run test:unit

# Run integration tests only
npm run test:integration

# Run tests with coverage
npm test -- --coverage

Docker Deployment

The project includes Docker support for easy deployment and development.

Prerequisites

  • Docker Engine 20.10+
  • Docker Compose 2.0+

Quick Start with Docker

  1. Copy environment variables
cp .env.example .env
  1. Configure your .env file with secure credentials

IMPORTANT: Replace the placeholder values with strong, unique passwords:

# Use strong passwords in production!
POSTGRES_USER=your_secure_username
POSTGRES_PASSWORD=your_secure_password_here
POSTGRES_DB=meta_backend
DATABASE_URL="postgresql://your_secure_username:your_secure_password_here@localhost:5432/meta_backend"
  1. Start services with Docker Compose
docker-compose up -d

This will start:

  • PostgreSQL database on port 5432
  • Fastify API server on port 3000

Security Note: The docker-compose.yml file requires environment variables to be set. Never commit real credentials to version control.

  1. Run database migrations
docker-compose exec app npx prisma migrate deploy --schema=./src/infrastructure/database/prisma/schema.prisma

Or use the npm script:

docker-compose exec app npm run prisma:migrate:deploy
  1. Access the application

Docker Commands

# Start services in detached mode
docker-compose up -d

# View logs
docker-compose logs -f app

# Stop services
docker-compose down

# Stop services and remove volumes
docker-compose down -v

# Rebuild containers
docker-compose build

# Execute commands in container
docker-compose exec app npm run prisma:generate
docker-compose exec app npm run prisma:migrate

# Open Prisma Studio (database GUI)
docker-compose exec app npm run prisma:studio

Environment Variables

The following environment variables must be configured in .env (they are required, no defaults are provided for security):

# Database Configuration (REQUIRED - use strong passwords!)
POSTGRES_USER=your_secure_username
POSTGRES_PASSWORD=your_secure_password
POSTGRES_DB=meta_backend
POSTGRES_PORT=5432
DATABASE_URL="postgresql://your_secure_username:your_secure_password@localhost:5432/meta_backend"

# Application Configuration
PORT=3000
NODE_ENV=production

Security Best Practices:

  • Use strong, unique passwords (minimum 16 characters, mix of letters, numbers, and symbols)
  • Never commit .env files to version control (already in .gitignore)
  • Use different credentials for development and production
  • Rotate passwords regularly
  • Consider using secrets management tools (AWS Secrets Manager, HashiCorp Vault, etc.) in production

Production Deployment

For production deployment, ensure:

  1. Use strong database passwords
  2. Set NODE_ENV=production
  3. Configure proper database connection strings
  4. Use environment-specific .env files
  5. Enable proper logging and monitoring

Troubleshooting

Prisma Schema Not Found

If you encounter errors about Prisma schema not being found, ensure you're using the npm scripts which include the --schema flag:

npm run prisma:generate
npm run prisma:migrate

Module Not Found After Build

If you see Cannot find module 'dist/main.js' error:

  1. Make sure you've built the project first:
npm run build
  1. Verify the dist folder exists and contains the compiled files

Database Connection Issues

  1. Start PostgreSQL with Docker (if not installed locally):
docker-compose up -d postgres
  1. Verify your .env file has the correct DATABASE_URL. For Docker, it should be:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/meta_backend"

Common mistake: If you see Authentication failed for user 'user', your .env has incorrect credentials. Make sure you're using postgres:postgres (not user:password).

  1. Ensure PostgreSQL is running and accessible:

    • Check Docker: docker-compose ps
    • Check locally: psql -U postgres -l
  2. Wait a few seconds after starting PostgreSQL before running migrations

Port Already in Use

If port 3000 is already in use:

  1. Stop the running process:

    • Windows: taskkill /F /IM node.exe (stops all Node processes) or netstat -ano | findstr :3000 then taskkill /PID <pid> /F
    • Linux/Mac: lsof -ti:3000 | xargs kill
  2. Or change the PORT in your .env file

Prisma Migration Timeout

If you get P1002: The database server timed out:

  1. Check if another migration is running: Close any other terminals running Prisma commands
  2. Restart PostgreSQL:
    docker-compose restart postgres
  3. Wait a few seconds before retrying the migration
  4. If the problem persists, check for locked connections:
    docker-compose exec postgres psql -U postgres -d meta_backend -c "SELECT * FROM pg_locks WHERE NOT granted;"

Fastify Instance Already Listening Error

If you see FST_ERR_INSTANCE_ALREADY_LISTENING:

  1. Stop the server completely: Press Ctrl+C in the terminal
  2. Kill any remaining Node processes: taskkill /F /IM node.exe (Windows)
  3. Restart the server: npm run dev
  4. This error usually happens when tsx watch restarts the server while it's still running

πŸ—οΈ Arquitectura y Decisiones TΓ©cnicas

Arquitectura Hexagonal (Clean Architecture)

Este proyecto implementa Hexagonal Architecture (tambiΓ©n conocida como Ports & Adapters), separando el dominio del negocio de los detalles de implementaciΓ³n.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    API Layer (Fastify)                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Controllers  β”‚  β”‚   Routes     β”‚  β”‚   Schemas    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Application Layer (Use Cases)              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ SyncAccounts β”‚  β”‚SyncCampaigns β”‚  β”‚ GetMetrics   β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                Domain Layer (Core)                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Entities    β”‚  β”‚ Repositories β”‚  β”‚    Errors    β”‚  β”‚
β”‚  β”‚  (Interfaces)β”‚  β”‚ (Interfaces) β”‚  β”‚  (Custom)    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Infrastructure Layer (Adapters)                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   Prisma     β”‚  β”‚ HTTP Client  β”‚  β”‚   Config     β”‚  β”‚
β”‚  β”‚ Repositories β”‚  β”‚ (Meta API)   β”‚  β”‚ (Fastify)    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Decisiones TΓ©cnicas Principales

1. Fastify en lugar de Express

ΒΏPor quΓ© Fastify?

  • Performance: Fastify es significativamente mΓ‘s rΓ‘pido que Express (2-3x en benchmarks)
  • TypeScript First: Mejor soporte nativo para TypeScript
  • Schema Validation: ValidaciΓ³n automΓ‘tica de schemas JSON con JSON Schema
  • Plugin System: Arquitectura de plugins mΓ‘s robusta y modular
  • Logging: Pino integrado para logging estructurado de alta performance

Trade-offs:

  • Menor ecosistema que Express (pero suficiente para este proyecto)
  • Curva de aprendizaje ligeramente mayor

2. Prisma en lugar de TypeORM/Sequelize

ΒΏPor quΓ© Prisma?

  • Type Safety: Genera tipos TypeScript automΓ‘ticamente desde el schema
  • Developer Experience: Migraciones automΓ‘ticas, Prisma Studio, mejor DX
  • Performance: Query builder optimizado, conexiones eficientes
  • Schema como Single Source of Truth: Un solo archivo define estructura y tipos
  • Moderno: Sintaxis intuitiva, menos boilerplate

Trade-offs:

  • Menos flexible que TypeORM para queries complejas (pero suficiente para este caso)
  • Learning curve para developers no familiarizados

3. Arquitectura Hexagonal

ΒΏPor quΓ© esta arquitectura?

  • Testabilidad: Cada capa puede testearse independientemente
  • Mantenibilidad: Cambios en infraestructura no afectan lΓ³gica de negocio
  • Escalabilidad: FΓ‘cil agregar nuevos adaptadores (REST, GraphQL, gRPC)
  • Desacoplamiento: El dominio no depende de frameworks externos
  • Clean Code: SeparaciΓ³n clara de responsabilidades

Trade-offs:

  • MΓ‘s archivos y estructura inicial (pero vale la pena para proyectos grandes)
  • Requiere mΓ‘s disciplina del equipo

4. TypeScript Strict Mode

ΒΏPor quΓ© TypeScript?

  • Type Safety: Detecta errores en tiempo de compilaciΓ³n
  • Autocomplete: Mejor experiencia de desarrollo con IDE
  • Refactoring Seguro: Cambios masivos con confianza
  • DocumentaciΓ³n ImplΓ­cita: Los tipos documentan el cΓ³digo

5. Dependency Injection Manual (DIContainer)

ΒΏPor quΓ© DI manual en lugar de librerΓ­a (InversifyJS, TSyringe)?

  • Simplicidad: No necesitamos decoradores complejos para este proyecto
  • Control Total: Sabemos exactamente quΓ© se estΓ‘ inyectando
  • Menos Dependencias: Menos librerΓ­as externas
  • Suficiente: Para el tamaΓ±o del proyecto, DI manual es suficiente

Trade-offs:

  • Si el proyecto crece mucho, podrΓ­a beneficiarse de una librerΓ­a DI
  • MΓ‘s cΓ³digo manual para mantener

6. Zod para ValidaciΓ³n

ΒΏPor quΓ© Zod?

  • Type Inference: Genera tipos TypeScript desde schemas
  • Runtime Validation: Valida datos en runtime, no solo en compile-time
  • Composable: Schemas pueden combinarse y reutilizarse
  • Mensajes de Error: Mensajes claros y ΓΊtiles
  • Lightweight: LibrerΓ­a pequeΓ±a y rΓ‘pida

7. Axios con Retry Logic

ΒΏPor quΓ© Axios + axios-retry?

  • Retry AutomΓ‘tico: Reintenta automΓ‘ticamente en errores transitorios
  • Exponential Backoff: Evita sobrecargar API externa
  • Interceptors: Facilita manejo centralizado de errores
  • TypeScript Support: Buen soporte para TypeScript

8. In-Memory Locks (LockService)

ΒΏPor quΓ© locks in-memory en lugar de Redis?

  • Simplicidad: No requiere infraestructura adicional para desarrollo
  • Suficiente: Para una sola instancia, funciona perfectamente
  • Extensible: FΓ‘cil migrar a Redis cuando se necesite escalar horizontalmente

Trade-off:

  • No funciona en mΓΊltiples instancias (pero se documenta cΓ³mo migrar a Redis)

9. Decimal en lugar de Float para Moneda

ΒΏPor quΓ© Decimal?

  • PrecisiΓ³n: Float tiene problemas de precisiΓ³n con decimales (0.1 + 0.2 β‰  0.3)
  • Moneda: Para valores monetarios, precisiΓ³n es crΓ­tica
  • Prisma Support: Prisma soporta Decimal nativamente

Trade-off:

  • Ligeramente mΓ‘s verboso en cΓ³digo (necesita .toNumber())

10. Vitest en lugar de Jest

ΒΏPor quΓ© Vitest?

  • Velocidad: MΓ‘s rΓ‘pido que Jest
  • TypeScript Native: Mejor integraciΓ³n con TypeScript
  • Compatible con Jest API: FΓ‘cil migraciΓ³n si alguien viene de Jest
  • Ecosystem: Compatible con herramientas de Jest

Patrones de DiseΓ±o Implementados

  1. Repository Pattern: Abstrae acceso a datos, permite cambiar ORM sin afectar lΓ³gica
  2. Use Case Pattern: Encapsula operaciones de negocio especΓ­ficas
  3. Dependency Injection: Facilita testing y desacoplamiento
  4. Strategy Pattern: LockService puede cambiar implementaciΓ³n (memory β†’ Redis)
  5. Error Handling con Clases: JerarquΓ­a de errores para manejo consistente

Estructura de Capas

  1. Domain Layer (src/domain/): Entidades, interfaces de repositorios, errores personalizados
  2. Application Layer (src/application/): Casos de uso, servicios de aplicaciΓ³n
  3. Infrastructure Layer (src/infrastructure/): Implementaciones (Prisma, HTTP, config)
  4. API Layer (src/api/): Controladores, rutas, validaciΓ³n de schemas

πŸ‘¨β€πŸ’» Autor

Juan Facundo Bazan Alvarez
Sr Backend Developer Node | Software Architect

Este proyecto fue desarrollado con arquitectura hexagonal limpia, desacoplada y lista para producciΓ³n.


Β© 2025 Juan Facundo Bazan Alvarez. Todos los derechos reservados.

About

Clean architecture backend built with Fastify, TypeScript, and Prisma. Synchronizes Meta Ads mock accounts, campaigns, and metrics with PostgreSQL, implementing hexagonal architecture and concurrency-safe sync flows.

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages