Estructura de carpetas — 14 microservicios (Sigfa / SigfaPro)¶
Patrón: monolito modular por dominio (vertical slice) dentro de cada servicio¶
Cada microservicio organiza su código por dominio de negocio, no por capa técnica. Router, service, repository, models y schemas de un mismo dominio viven juntos en su propia carpeta. Esto vale para los 14, incluidos los de un solo dominio — así el criterio es único en todo el proyecto y cualquier dominio se puede mover/extraer sin desarmar nada.
Patrón común¶
svc-<nombre>/
├── app/
│ ├── main.py # arranque FastAPI, registro de routers de cada dominio
│ ├── core/
│ │ ├── config.py # variables de entorno (Pydantic Settings)
│ │ ├── security.py # valida JWT emitido por svc-identidad
│ │ ├── permissions.py # require_permission() por endpoint
│ │ ├── exceptions.py # excepciones de negocio -> HTTP errors
│ │ └── logging.py # logging estructurado (JSON)
│ ├── api/
│ │ └── deps.py # get_db(), get_current_user() — transversal, no es un dominio
│ ├── domains/
│ │ ├── <dominio_1>/
│ │ │ ├── router.py # endpoints del dominio
│ │ │ ├── service.py # lógica de negocio, sin SQL
│ │ │ ├── repository.py # único lugar que toca SQL de este dominio
│ │ │ ├── models.py # entidades SQLAlchemy
│ │ │ └── schemas.py # contratos Pydantic
│ │ ├── <dominio_2>/
│ │ │ └── ... (misma forma)
│ │ └── ...
│ ├── shared/ # SOLO lo que de verdad cruza 2+ dominios de ESTE servicio
│ │ └── ... # (si un dominio importa de otro dominio, es señal de alerta)
│ └── events/
│ ├── publisher.py # outbox: publica eventos de dominio hacia otros servicios
│ └── subscriber.py # consume eventos de otros servicios (si aplica)
├── alembic/
│ ├── versions/ # migraciones versionadas (una por cambio)
│ └── env.py
├── tests/
│ ├── unit/
│ │ └── <dominio>/ # mismo árbol que domains/, por dominio
│ └── integration/
│ └── <dominio>/
├── docs/
│ └── adr/ # 0001-titulo.md — decisiones de arquitectura de ESTE servicio
├── pyproject.toml
├── .env.example # nunca .env real committeado
└── Dockerfile
Regla dura: un archivo dentro de domains/nomina/ no importa directo de
domains/asistencia/. Si dos dominios necesitan comunicarse, es a través de shared/ (síncrono,
dentro del mismo servicio) o de events/ (asíncrono, entre servicios). Si notás que la única forma
de resolver algo es importar cruzado entre dominios, esa es la señal de que en realidad son un solo
dominio (o de que falta un evento).
CORE DE PLATAFORMA (BD: core_sigfa)¶
1. svc-identidad¶
app/domains/
├── usuarios/ # login, tokens, credenciales
├── empresas/
├── sucursales/
└── permisos/ # tipos de usuario y permisos individuales, SSO
2. svc-workflow¶
app/domains/
└── casos/ # estado y aprobaciones/seguimiento (no el efecto de negocio)
3. svc-notificaciones (ya existe)¶
app/domains/
└── notificaciones/ # WhatsApp, marcaciones biométricas
4. svc-documents (generación de documentos/PDF)¶
app/domains/
├── pdf/ # facturas, etiquetas, reportes (WeasyPrint)
├── excel/ # exportaciones contables e inventariales (openpyxl)
└── word/ # plantillas editables (python-docx)
ERP — sigfa.com.ec (BD: erp_sigfa)¶
5. svc-operaciones (6 dominios, 1 transacción)¶
app/domains/
├── inventario/
├── recepcion_mp/
├── produccion/ # planificación + ejecución
├── envasado/
├── etiquetado/
└── calidad/
app/shared/produccion_orquestador.py (o similar), que sí puede tocar los 6 repositories para
garantizar la atomicidad. Vale un ADR (0001-transaccion-cruzada-operaciones.md) documentando por
qué este es el único lugar del servicio donde se permite cruzar dominios.
6. svc-pedidos¶
app/domains/
├── pedidos/ # pedido de despacho
├── canal_app/ # canal B2C / app
└── tienda/ # catálogo (hoy fusionado con la app)
7. svc-contable¶
app/domains/
├── facturacion/ # facturación, CxC
└── contabilidad/
svc-integraciones
(#10), para aislar sus fallas del resto de la contabilidad.
8. svc-compras¶
app/domains/
└── compras/ # órdenes, proveedores, cuentas por pagar
9. svc-rrhh (RRHH / Nómina — incluye faena pesquera)¶
app/domains/
├── personal/ # legajo/ficha
├── nomina/ # cálculo/pago de nómina
├── asistencia/ # manual + biométrica (evento desde svc-notificaciones)
├── empleados/ # comisariato, deducciones
└── faena_pesquera/ # liquidación de faena (se calcula junto a nómina, pero es su propio dominio)
10. svc-activos (Mantenimiento de activos)¶
app/domains/
├── mantenimiento/ # preventivo/correctivo
├── odt/ # órdenes de trabajo
└── gastos/ # gastos por activo
11. svc-integraciones (Integraciones contables — "falla afuera")¶
app/domains/
├── aylen/
├── qbo/
└── siigo/
domains/<proveedor>/service.py maneja su propio circuito de reintentos/timeouts, y
app/shared/ puede tener un circuit_breaker.py genérico que los tres reutilizan.
12. svc-reportes (Reportes y analítica — "solo lectura")¶
app/domains/
└── gerencial/ # consolidados gerenciales
gerencial/repository.py solo lee — arma proyecciones a partir de eventos consumidos de los demás
servicios, nunca escribe hacia atrás a ningún otro dominio.
CRM — sigfapro.com.ec (BD: crm_sigfapro, desglose interno según el plan del CRM)¶
13. svc-clientes¶
app/domains/
└── clientes/ # cartera, contactos, historial
14. svc-fuerza-ventas¶
app/domains/
└── fuerza_ventas/ # promotores, preventa
Qué queda fuera de los domains/ (infraestructura transversal, no es negocio)¶
En el monolito, shared/ tenía comunas_service.py y pdf_header_service.py (usado por 10+
módulos). Al partir en microservicios, esto no debe copiarse dentro de cada uno — conviene sacarlo
a una librería interna compartida (paquete pip privado o git submodule) que cada svc-* importe,
para no duplicar lógica ni sincronizar 14 copias del mismo encabezado de PDF. Vale un ADR
(0002-libreria-compartida.md) definiendo dónde vive y cómo se versiona — y otro
(0003-organizacion-por-dominio.md) dejando asentado que la organización interna de todos los
servicios es por dominio, no por capa, ya que es un patrón que se repite en más de un módulo.