Skip to content

Estándares de Plataforma

Variables de Entorno

Categorías

CategoríaEjemploEn código
URLs de serviciosDATABASE_WRITE_URL, RABBITMQ_URLRequerida (fail-fast)
SecretsCLIENT_SECRET, HMAC_SECRETRequerida (fail-fast)
Configuración operacionalTENANT_CACHE_TTL_SECONDSCon default
Feature flagsENABLE_MAKER_CHECKERCon default (false)

Naming de Variables

{SERVICE}_{COMPONENT}_{PROPERTY}

Ejemplos:
DATABASE_WRITE_URL
DATABASE_READ_URL
RABBITMQ_URL
RABBITMQ_USER
RABBITMQ_PASSWORD
KEYCLOAK_URL
KEYCLOAK_CLIENT_ID
KEYCLOAK_CLIENT_SECRET
REDIS_URL
REDIS_KEY_PREFIX

Validación al Startup (Fail-Fast)

csharp
public class EnvConfig
{
    public string DatabaseWriteUrl { get; } = Required("DATABASE_WRITE_URL");
    public string DatabaseReadUrl { get; } = Required("DATABASE_READ_URL");
    public string RabbitMqUrl { get; } = Required("RABBITMQ_URL");
    public int TenantCacheTtl { get; } = Optional("TENANT_CACHE_TTL_SECONDS", 300);

    private static string Required(string name) =>
        Environment.GetEnvironmentVariable(name) 
        ?? throw new InvalidOperationException($"Missing required env var: {name}");
}

Health Checks

Cada servicio expone:

EndpointPropósitoAuth
/healthHealth check completo (BD + Redis + RabbitMQ)Ninguna
/health/readyReadiness (puede recibir tráfico)Ninguna
/health/liveLiveness (proceso vivo)Ninguna
csharp
builder.Services.AddHealthChecks()
    .AddNpgSql(config.DatabaseWriteUrl, name: "postgresql")
    .AddRedis(config.RedisUrl, name: "redis")
    .AddRabbitMQ(config.RabbitMqUrl, name: "rabbitmq");

Logging

Formato

Structured logging con Serilog. Output JSON en producción, console en desarrollo.

csharp
Log.Logger = new LoggerConfiguration()
    .Enrich.WithProperty("Service", "imagy-lending")
    .Enrich.WithProperty("Environment", env)
    .WriteTo.Console(new JsonFormatter()) // producción
    .CreateLogger();

Campos obligatorios en cada log

CampoFuenteEjemplo
ServiceConfiguraciónimagy-lending
RequestIdHeader o generadoa1b2c3d4
TenantIdIIdentityContextuuid
UserIdIIdentityContextuuid
CorrelationIdHeader o eventouuid

Niveles

NivelCuándo
ErrorExcepciones no manejadas, fallos de integración
WarningSituaciones inesperadas pero recuperables
InformationOperaciones de negocio completadas
DebugDetalles técnicos (solo en desarrollo)

Nunca loguear

  • Passwords o secrets
  • Tokens JWT completos
  • Datos biométricos
  • Números de documento completos (solo últimos 4 dígitos)

Docker

Multi-stage Build (patrón estándar)

dockerfile
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS runtime
WORKDIR /app
COPY --from=build /app .
USER app
EXPOSE 8080
ENTRYPOINT ["dotnet", "Imagy.{Domain}.Api.dll"]

Reglas

  • Base image: siempre Alpine (mínima superficie de ataque)
  • Non-root user: siempre USER app
  • Puerto: 8080 (estándar ECS Fargate)
  • No secrets en la imagen
  • .dockerignore actualizado

Docker Compose (desarrollo local)

Cada servicio tiene su docker-compose.yml para desarrollo local. Además existe un docker-compose.infra.yml compartido para la infraestructura:

yaml
# docker-compose.infra.yml (en imagy-infra o en cada repo)
services:
  postgres:
    image: postgres:16-alpine
    ports: ["5432:5432"]
    environment:
      POSTGRES_PASSWORD: localdev
    volumes:
      - pgdata:/var/lib/postgresql/data

  valkey:
    image: valkey/valkey:8-alpine
    ports: ["6379:6379"]

  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports: ["5672:5672", "15672:15672"]

  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    ports: ["8080:8080"]
    command: start-dev
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin

volumes:
  pgdata:

Respuestas API (Envelope estándar)

Éxito

json
{
  "data": { ... },
  "metadata": {
    "request_id": "a1b2c3d4",
    "timestamp": "2026-05-18T10:30:00Z"
  }
}

Éxito paginado

json
{
  "data": [ ... ],
  "metadata": {
    "request_id": "a1b2c3d4",
    "timestamp": "2026-05-18T10:30:00Z",
    "pagination": {
      "page": 1,
      "page_size": 20,
      "total": 150,
      "total_pages": 8
    }
  }
}

Error

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "One or more validation errors occurred",
    "details": [
      { "field": "amount", "message": "Must be greater than 0" }
    ]
  },
  "metadata": {
    "request_id": "a1b2c3d4",
    "timestamp": "2026-05-18T10:30:00Z"
  }
}

Códigos de Error Estándar

CódigoHTTP StatusDescripción
VALIDATION_ERROR400Datos de entrada inválidos
UNAUTHORIZED401No autenticado
FORBIDDEN403Sin permisos
NOT_FOUND404Recurso no existe
CONFLICT409Conflicto de estado
RATE_LIMITED429Rate limit excedido
INTERNAL_ERROR500Error interno
SERVICE_UNAVAILABLE503Servicio no disponible

Reimagine Tech LLC — Documentacion Interna