Arquitectura¶
Stack¶
- Backend: Python 3.12 · FastAPI · SQLAlchemy 2.0 async (
asyncpg) · Pydantic v2 · Alembic · structlog. - Base de datos: PostgreSQL (UE).
- Frontend: Angular 20 + Ionic (standalone components, signals).
- Infra: Docker / docker-compose tras nginx-proxy + letsencrypt.
- Terceros: Twilio (voz), Vapi (plataforma de voz), Claude (Anthropic), ElevenLabs, Telegram, email transaccional.
El backend no procesa audio: recibe webhooks (datos) de Vapi y del formulario web.
Hexagonal (backend app/)¶
domain/ # sin dependencias de framework
entities/ # modelos de dominio (sin ORM)
ports/ # interfaces (Protocol): LeadClassifier, Notifier…
services/ # lógica de negocio (clasificación, enrutado, permisos)
adapters/http/
routers/ # un *_router.py por recurso
dto/ # esquemas Pydantic de request/response
webhooks/ # Vapi + formulario web
infrastructure/
persistence/ # models.py (ORM) + repositories/ (uno por agregado, filtran por tenant)
voice/ claude/ telegram/ email/ captcha/
Reglas de dependencia
domain/solo importa dedomain/y stdlib. Nuncaadapters/infrastructure/SQLAlchemy.- Los routers llaman a
domain/servicesy arepositories; no importan ORM ni ejecutan SQL directo. - Los repos reciben el tenant (
organization_id/dealership_id) y filtran en la query.
Errores¶
Todo error recuperable se convierte en una variante de AppError (BadRequest 400, Unauthorized 401, Forbidden 403, NotFound 404, Conflict 409, Internal 500) y se serializa como {"error": <mensaje>}. Los errores internos se loguean con contexto y nunca exponen el detalle al cliente.
Seguridad¶
- Contraseñas con Argon2id; login en tiempo constante.
- JWT HS256; una cuenta suspendida deja de tener acceso de inmediato (se comprueba en cada petición, no solo al login).
- CORS restrictivo; rate limiting (slowapi) y captcha (Turnstile, fail-closed) en los endpoints públicos.
- Aislamiento por tenant en cada consulta.
Frontend¶
- Componentes standalone, signals para estado,
takeUntilDestroyeden las suscripciones. - Componentes propios en
shared/sobre Ionic estándar (sin librerías privadas externas). - Tokens CSS
--mx-*+ variables Ionic--ion-color-*;font-sizeenrem.data-testiden formatogk-<componente>-<elemento>.