En una primera versión es razonable avanzar con pocas piezas. El riesgo aparece cuando la velocidad inicial se convierte en una arquitectura accidental: controladores que deciden todo, consultas duplicadas, estados escritos como texto libre y automatizaciones que actualizan datos sin pasar por las mismas reglas.
La alternativa no exige microservicios. Para muchos equipos, un monolito modular bien diseñado ofrece un límite claro entre dominios, una sola operación desplegable y suficiente disciplina para crecer.
1) Modular no significa repartir archivos por tipo
Separar carpetas en controllers, services y models ordena el código, pero no define responsabilidades de negocio. Un módulo útil representa una capacidad: solicitudes, aprobaciones, facturación, inventario o identidad.
Cada módulo debe exponer casos de uso deliberados y proteger sus invariantes. Otros módulos no deberían modificar directamente sus tablas sólo porque comparten PostgreSQL.
2) Cuatro capas con responsabilidades verificables
| Capa | Responsabilidad | Señal de alerta |
|---|---|---|
| Interfaz / API | Autenticar, validar formato y traducir la solicitud | Contiene decisiones financieras u operativas |
| Aplicación | Coordinar el caso de uso y su transacción | Depende de detalles de HTTP o de la pantalla |
| Dominio | Estados, reglas e invariantes | Las reglas viven sólo en formularios |
| Infraestructura | Persistencia, correo, archivos y APIs externas | Un proveedor dicta el modelo de negocio |
3) La base de datos también protege el proceso
TypeScript reduce errores durante el desarrollo; no evita dos escrituras concurrentes ni datos insertados por otro canal. Claves foráneas, restricciones únicas, tipos adecuados y transacciones deben respaldar las reglas que no pueden romperse.
Prisma facilita acceso tipado y migraciones, pero el esquema sigue siendo una decisión de producto. Conviene revisar índices, cardinalidad, bloqueos y planes de migración en vez de tratar el ORM como sustituto del diseño de datos.
4) Modelar comandos y consultas por separado
Una pantalla operativa puede requerir una consulta agregada y optimizada; aprobar una solicitud requiere validar permisos, estado vigente y reglas de transición. Forzar ambos recorridos a compartir el mismo objeto produce acoplamiento innecesario.
No hace falta implementar CQRS completo. Basta con distinguir lecturas orientadas a vistas de comandos que cambian estado. Asà se optimizan reportes sin debilitar las reglas de escritura.
5) Transacciones cortas; efectos externos después
Enviar correo o invocar una API dentro de una transacción mantiene bloqueos mientras se espera a otro sistema. Una opción más robusta es guardar el cambio y un evento pendiente en la misma transacción; un worker procesa después la notificación con idempotencia y reintentos.
n8n puede orquestar tareas externas cuando aporta visibilidad operativa. El backend conserva la autoridad sobre estados y reglas; el workflow recibe eventos y devuelve resultados mediante contratos explícitos.
6) Permisos ligados a acciones, no sólo a pantallas
Ocultar un botón mejora la experiencia, pero no es control de acceso. El backend debe autorizar cada comando según identidad, rol, alcance y estado del registro. Acciones sensibles necesitan registrar actor, momento, motivo y valores relevantes antes y después.
7) Contratos y errores que ayuden a operar
Una API mantenible usa esquemas versionables, códigos de error estables y un identificador de correlación. El usuario necesita saber qué corregir; soporte necesita encontrar el recorrido; desarrollo necesita contexto técnico sin exponer secretos.
- Errores de validación con campos y reglas comprensibles.
- Conflictos de versión cuando otro usuario ya modificó el registro.
- Resultados idempotentes para comandos repetidos.
- Logs estructurados conectados con caso, evento y actor.
8) Probar fronteras de riesgo
La cobertura total es menos útil que pruebas deliberadas sobre invariantes: transiciones inválidas, concurrencia, permisos, redondeos, duplicados y reintentos. Las integraciones externas necesitan pruebas de contrato y escenarios de timeout o respuesta parcial.
9) Un camino incremental
- Definir módulos a partir de capacidades y responsables del proceso.
- Elegir un flujo crítico y escribir estados, invariantes y permisos.
- Encapsular sus casos de uso antes de reorganizar todo el sistema.
- Respaldar reglas críticas con restricciones y transacciones.
- Extraer efectos externos a una cola o patrón outbox.
- Medir errores, tiempos y excepciones antes de dividir despliegues.
10) Mantenibilidad es capacidad de cambiar con control
Una buena arquitectura interna no intenta anticipar cada requisito. Hace visible dónde vive una regla, limita el impacto de modificarla y deja evidencia cuando el proceso cambia.
Node.js, TypeScript, PostgreSQL y Prisma forman una base productiva, pero la ventaja aparece cuando la estructura técnica refleja responsabilidades operativas reales. Ese criterio permite crecer sin convertir cada nueva pantalla en deuda futura.