En este artículo
La arquitectura empieza con decisiones, no con carpetas
La IA puede producir muchas líneas en poco tiempo, pero no decide qué cambios tendrá que soportar el producto ni qué riesgos acepta el equipo. Antes de pedir código conviene escribir las fuerzas reales: reglas de negocio, fuentes de datos, integraciones, volumen esperado, requisitos de seguridad y forma de despliegue. Esa descripción permite separar lo estable de lo sustituible. Un framework web cambia con más facilidad que la regla que calcula si una operación está permitida.
Un asistente de programación trabaja mejor cuando recibe límites concretos. Hay que indicarle el módulo afectado, las dependencias permitidas, los contratos existentes y cómo se verificará el resultado. «Crea una arquitectura escalable» invita a introducir capas y servicios hipotéticos. «Añade este caso de uso al monolito, sin nuevas dependencias y manteniendo el dominio independiente del adaptador HTTP» conduce a un cambio revisable.
Clean architecture con IA
La arquitectura limpia organiza dependencias alrededor del dominio. Las reglas de negocio ocupan el centro; los casos de uso las coordinan; los adaptadores conectan HTTP, base de datos, colas o proveedores externos. La regla importante no es el nombre de las carpetas, sino la dirección: el dominio no importa FastAPI, Supabase ni un SDK de inteligencia artificial. Los detalles conocen el núcleo, pero el núcleo no conoce los detalles.
src/
domain/ # entidades, invariantes y errores
application/ # casos de uso y contratos necesarios
adapters/
http/ # rutas y esquemas de transporte
persistence/ # repositorios PostgreSQL
ai/ # cliente del modelo y traducción de respuestas
main.py # composición de dependencias
Esta separación es especialmente útil con IA porque el código generado tiende a acoplar lo que tiene a la vista. Si un endpoint, una consulta SQL y una llamada al modelo aparecen en el mismo prompt, es probable que terminen en una sola función. Dar al asistente un contrato estrecho evita ese resultado. El caso de uso recibe interfaces simples y devuelve un resultado de dominio; el adaptador traduce errores a HTTP; el cliente del modelo convierte una respuesta externa en datos validados.
No conviertas cada función en una interfaz
La arquitectura limpia también puede sobredimensionarse. Una interfaz se justifica cuando separa una dependencia externa, habilita una prueba relevante o existen implementaciones intercambiables de verdad. Añadir repositorios genéricos, factorías y servicios vacíos solo para cumplir un diagrama aumenta la distancia entre la intención y el comportamiento. La estructura correcta es la mínima que mantiene claras las dependencias.
Monolith first: una unidad desplegable bien modularizada
Empezar con un monolito reduce coordinación, observabilidad distribuida y fallos de red. Un único proceso puede contener módulos de dominio claros, una base de datos y trabajos en segundo plano sin convertirse en una masa desordenada. «Monolito» describe el despliegue; «modular» describe el diseño interno. Ambos conceptos encajan.
Los microservicios añaden contratos remotos, autenticación entre servicios, reintentos, trazas distribuidas, despliegues coordinados y consistencia eventual. La IA puede generar los manifiestos y clientes, pero no elimina ese coste operativo. Separar un servicio tiene sentido cuando existe una frontera de negocio estable, una necesidad de escalado independiente o un ciclo de entrega que realmente lo exige. Hasta entonces, una llamada de función y una transacción local suelen ser más fiables.
| Señal | Respuesta inicial | Cuándo extraer |
|---|---|---|
| Módulos con reglas distintas | Paquetes internos | Equipos y despliegues independientes |
| Tarea pesada | Worker del mismo sistema | Escalado o aislamiento demostrado |
| Integración externa | Adaptador | Producto autónomo con contrato estable |
| Base de datos grande | Índices y consultas | Necesidad medida, no intuida |
Diseño de APIs REST que resiste cambios
Una API es un contrato para consumidores, no un reflejo directo de las tablas. Los recursos se nombran con sustantivos, los métodos HTTP expresan la operación y los códigos de estado distinguen éxito, errores del cliente y fallos internos. Las respuestas usan un formato coherente; la paginación y los filtros tienen límites; la autenticación identifica al actor, mientras que la autorización comprueba si puede operar sobre ese recurso.
GET /api/v1/projects/{project_id}/tasks?status=pending
POST /api/v1/projects/{project_id}/tasks
PATCH /api/v1/tasks/{task_id}
DELETE /api/v1/tasks/{task_id}
# Error estable para clientes
{
"error": "task_not_found",
"message": "La tarea no existe o no es accesible",
"request_id": "..."
}
Los esquemas de entrada establecen longitud, tipos y valores admitidos. Los de salida impiden filtrar accidentalmente columnas internas. Una actualización parcial no debe sobrescribir campos ausentes, y una operación idempotente debe producir el mismo estado al repetirse. Versionar en la URL hace visible un cambio incompatible, pero no sustituye una política de evolución: primero se añaden campos opcionales, se mide el uso y solo después se retira lo antiguo.
Cómo pedir una API a la IA
El prompt debe incluir ejemplos válidos e inválidos, códigos esperados, reglas de autorización y nombres de errores. Después se revisan rutas duplicadas, estados ambiguos y dependencias de infraestructura introducidas en el dominio. La especificación generada puede ayudar a descubrir inconsistencias, pero el contrato debe aprobarlo quien mantiene a consumidores y servidor.
Patrones útiles y patrones prematuros
Un repositorio aísla persistencia cuando el dominio no debería depender de SQL. Un adaptador encapsula proveedores externos. Un patrón estrategia sirve si varias políticas de negocio cambian de forma independiente. Una cola desacopla trabajo que puede completarse después. Ninguno debe aplicarse solo porque el modelo reconozca su nombre.
En sistemas con agentes, Coordinator y Workers puede separar la decisión de alto nivel de tareas mecánicas. El coordinador divide un objetivo, asigna capacidades limitadas y combina resultados; cada worker recibe el contexto mínimo. Para una sola llamada determinista, ese patrón sobra. También conviene evitar el «agente universal» con todas las herramientas: dificulta autorización, evaluación y diagnóstico.
- Usa una función cuando una función expresa toda la regla.
- Usa un módulo cuando varias funciones comparten una responsabilidad.
- Introduce un contrato cuando protege el dominio de un detalle externo.
- Extrae un servicio cuando la frontera operativa ya existe y se puede medir.
Refactoring asistido por IA sin cambiar comportamiento
Refactorizar con IA funciona mejor en pasos pequeños. Primero se captura el comportamiento observable con pruebas o una reproducción. Después se pide un único movimiento: extraer una función, invertir una dependencia o eliminar duplicación. Se revisa el diff y se ejecuta la misma verificación. Mezclar refactor, nueva funcionalidad y actualización masiva de dependencias hace imposible atribuir un fallo.
Objetivo: mover la validación de Task al dominio.
Debe mantenerse:
- mismos resultados para entradas válidas;
- mismos códigos de error públicos;
- ninguna dependencia de FastAPI en domain/.
Alcance: task.py y sus pruebas directas.
No hagas: renombrados globales ni cambios de formato.
El asistente puede localizar duplicación, proponer nombres y explicar dependencias, pero hay que comprobar todos los llamadores. Una extracción incompleta crea dos convenciones paralelas. El corte limpio migra consumidores, elimina el camino obsoleto y conserva una sola fuente de verdad. Las métricas útiles son complejidad, acoplamiento y tiempo de cambio, no el número de patrones introducidos.
Arquitectura de testing: confianza por capas
Las pruebas siguen la forma del sistema. Las reglas puras del dominio se comprueban rápido y sin red. Las pruebas de integración ejercitan adaptadores reales, como consultas y migraciones, en un entorno aislado. Las pruebas de contrato verifican que cliente y servidor interpretan igual una API. Un recorrido end to end confirma pocos flujos críticos desde la entrada hasta el efecto final.
| Nivel | Qué protege | Dobles |
|---|---|---|
| Unidad | Invariantes y transiciones | Solo límites externos |
| Integración | SQL, serialización, proveedor | Servicios ajenos si es necesario |
| Contrato | Esquema y compatibilidad | Consumidor o servidor controlado |
| E2E | Flujos de negocio esenciales | Los mínimos posibles |
En funciones que dependen de un modelo, no conviene afirmar una frase exacta. Se valida el esquema, la política y el efecto permitido. Un conjunto de evaluaciones con entradas normales y adversarias detecta regresiones del prompt o del modelo. Para decisiones críticas, el test comprueba el guardarraíl determinista: una salida que solicita una acción prohibida debe ser rechazada aunque parezca convincente.
ADRs y diseño de datos: memoria del equipo
Un Architecture Decisión Record conserva contexto, opciones, decisión y consecuencias. Es breve y se escribe cuando la decisión todavía se entiende. «Usamos PostgreSQL porque necesitamos transacciones y el equipo ya lo opera» ayuda más que un documento que solo describe el estado final. La IA puede preparar un borrador desde una conversación, pero no debe inventar restricciones ni consenso.
El diseño de base de datos forma parte de la arquitectura. Las claves, restricciones y relaciones expresan invariantes cerca de los datos. La IA puede proponer un esquema, pero se revisan cardinalidad, nulabilidad, borrado, aislamiento entre usuarios e índices ligados a consultas reales. Un modelo de dominio limpio no compensa una tabla que permite estados imposibles.
Checklist para una evolución sostenible
- ¿Las dependencias apuntan al dominio?
- ¿El monolito mantiene fronteras internas claras?
- ¿La API tiene contratos y errores estables?
- ¿Cada patrón resuelve un problema presente?
- ¿El refactor conserva comportamiento verificado?
- ¿Las pruebas cubren reglas, adaptadores y flujos críticos?
- ¿Las decisiones importantes tienen contexto escrito?
La IA acelera la implementación cuando la arquitectura reduce sus grados de libertad. El objetivo no es producir más capas, sino hacer que la siguiente modificación resulte predecible. El módulo de Arquitectura de Software con IA lleva este enfoque a un proyecto completo.
Preguntas frecuentes
¿Clean architecture exige muchas carpetas?
No. Exige que las reglas de negocio no dependan de detalles externos. Un proyecto pequeño puede cumplirla con pocos módulos y contratos solo en las fronteras necesarias.
¿Cuándo conviene pasar de monolito a microservicios?
Cuando una frontera estable necesita despliegue, aislamiento o escalado independiente y el beneficio compensa la complejidad distribuida. No por anticipar crecimiento.
¿REST es suficiente para una aplicación con IA?
Normalmente sí. La presencia de un modelo no cambia los fundamentos del contrato HTTP. Lo importante es validar entradas, salidas, autorización y errores.
¿Puede la IA decidir qué patrón de diseño usar?
Puede proponer opciones y consecuencias, pero la elección depende de fuerzas del producto y de operación que deben aportar y revisar las personas responsables.
¿Cómo refactorizar código generado por IA con seguridad?
Con cambios pequeños, comportamiento capturado, revisión del diff, migración completa de llamadores y verificación después de cada paso.
¿Cómo se prueban respuestas no deterministas?
Se validan esquemas, invariantes, políticas y efectos permitidos. Las evaluaciones semánticas complementan, pero no sustituyen, los controles deterministas.
📚 Aprende más en el curso
Este artículo complementa el Módulo M14: Arquitectura de Software con IA. Incluye vídeo, quiz, flashcards con repaso espaciado y proyecto práctico.