Arquitectura de aplicaciones de software
Resumen
La arquitectura de una aplicación reúne las decisiones estructurales costosas de cambiar: qué componentes existen, cómo se relacionan, qué responsabilidad asume cada uno y qué propiedades de calidad debe sostener el sistema. No es un organigrama ni una colección de diagramas, sino el conjunto de acuerdos que permite construir y evolucionar el software.
Esta guía presenta los conceptos que un profesional de TI debe reconocer al participar en decisiones de arquitectura: estilos estructurales, separación de responsabilidades, monolito y microservicios, escalabilidad y documentación con C4. El enfoque es conceptual y orientado a reconocer compromisos.
Material educativo independiente. No está afiliado, patrocinado ni respaldado por The Linux Foundation. LFCA es una marca de sus respectivos titulares.
Objetivos de aprendizaje
- Distinguir arquitectura de diseño detallado e implementación.
- Explicar la separación de responsabilidades, el acoplamiento y la cohesión.
- Comparar monolito, monolito modular y microservicios según contexto y operación.
- Relacionar escalabilidad horizontal, estado y balanceo de carga.
- Reconocer los niveles de C4 y el propósito de un registro de decisiones.
Prerrequisitos y conexiones
Conviene comprender la diferencia entre proyecto y operación, y el papel de los requisitos funcionales y no funcionales. El análisis funcional aporta el qué; la arquitectura decide el cómo estructural. Los temas de administración de sistemas y cloud explican después cómo se despliega y opera lo diseñado.
Qué es arquitectura de software
La norma ISO/IEC/IEEE 42010 describe la arquitectura como la organización fundamental de un sistema: sus elementos, relaciones y principios de diseño y evolución. Esa definición incluye tres ideas:
- Estructura: componentes, módulos, servicios, capas y datos.
- Relaciones: contratos, dependencias, flujos de datos y protocolos.
- Racionalidad: por qué se eligió esa estructura y qué restricciones la condicionan.
La arquitectura se ocupa de decisiones con alto costo de reversión, como el estilo general, el modelo de datos o los límites entre dominios. El diseño detallado resuelve decisiones locales y más fáciles de cambiar; la implementación es el código concreto.
Toda arquitectura es un ejercicio de compromisos: mejorar disponibilidad suele aumentar costo y complejidad, y centralizar datos simplifica la consistencia pero limita la autonomía. No existe una arquitectura correcta en abstracto, sino la más adecuada para un contexto y unos requisitos de calidad.
Requisitos de calidad y restricciones
Los requisitos funcionales indican qué debe hacer el sistema; los no funcionales o atributos de calidad indican cómo debe comportarse: rendimiento, disponibilidad, seguridad, mantenibilidad u observabilidad. La arquitectura debe hacerlos explícitos porque condicionan la estructura.
Las restricciones son límites impuestos: presupuesto, normativa, tecnología autorizada, latencia o habilidades del equipo. Ignorarlas produce diseños elegantes pero inviables.
Estilos y estructuras habituales
Un estilo arquitectónico es un patrón de organización probado. No son excluyentes: un sistema real puede combinar varios.
| Estilo | Idea central | Ventaja principal | Riesgo típico |
|---|---|---|---|
| Capas | Organizar por responsabilidad: presentación, aplicación, dominio, datos | Separación de responsabilidades y sustitución de capas | Capas artificiales y dependencias cruzadas |
| Cliente-servidor | Un cliente solicita, un servidor responde | Simplicidad y control central | El servidor se convierte en cuello de botella |
| Monolito | Una sola unidad desplegable | Operación y transacciones simples | Acoplamiento y escalado global |
| Microservicios | Servicios pequeños, autónomos y desplegables por separado | Autonomía de equipos y escalado selectivo | Complejidad distribuida y consistencia eventual |
| Eventos | Los componentes publican y consumen eventos | Desacoplamiento y procesamiento asíncrono | Trazabilidad y orden de eventos difíciles |
Capas y separación de responsabilidades
En una arquitectura en capas, cada nivel agrupa responsabilidades relacionadas: presentación, aplicación, dominio e infraestructura. La regla sana es que las dependencias apunten hacia el núcleo de negocio: el dominio no debería conocer detalles de la interfaz web ni del motor de base de datos.
El principio de separación de responsabilidades busca reducir el acoplamiento y aumentar la cohesión. Una capa cohesiva tiene una razón clara para cambiar; un acoplamiento bajo permite modificarla sin propagar efectos. No se trata de crear capas por moda, sino de aislar lo que cambia por motivos distintos.
Monolito, monolito modular y microservicios
El monolito es una aplicación con una sola unidad desplegable. Puede estar bien o mal estructurado. Con módulos internos claros, interfaces explícitas y pruebas automatizadas es una opción madura y económica: ofrece transacciones locales sencillas, un solo pipeline y menor sobrecarga operativa.
El monolito modular conserva un único despliegue, pero organiza el código en módulos con límites controlados. Es una ruta frecuente cuando el dominio se está descubriendo, porque permite aprender antes de asumir el costo de distribuir.
Los microservicios dividen el sistema en servicios autónomos alineados con capacidades de negocio. Cada uno puede desplegarse y escalarse por separado, y suele tener su propio almacenamiento y contrato. Esto favorece la autonomía de equipos, pero introduce latencia de red, fallos parciales, transacciones distribuidas, versionado, observabilidad y mayor complejidad operativa.
La decisión no es moderno contra obsoleto. Un equipo pequeño con dominio inestable suele beneficiarse de un monolito modular; una organización grande con dominios delimitados puede justificarlos. Adoptarlos sin límites claros multiplica el costo.
Escalabilidad, disponibilidad y estado
Escalar verticalmente significa aumentar los recursos de una máquina: más CPU, memoria o disco. Es sencillo, pero tiene un límite físico y suele implicar un único punto de fallo. Escalar horizontalmente significa añadir más instancias y distribuir la carga. Es la base de la elasticidad en cloud, pero exige que las instancias puedan compartir o externalizar el estado.
Una aplicación sin estado no guarda datos de sesión en la memoria o el disco local de una instancia. Si la sesión se externaliza a una base de datos, una caché compartida o un token firmado, cualquier instancia puede atender cualquier petición. Esa propiedad facilita el balanceo, el reemplazo de instancias y la recuperación ante fallos.
El principio de 12-Factor App añade prácticas como configuración en variables de entorno, procesos sin estado y separación entre construcción y ejecución. No es una certificación obligatoria, pero ayuda a razonar sobre aplicaciones desplegables en la nube.
La disponibilidad se fortalece con redundancia, aislamiento de fallos, tiempos de espera, reintentos con retroceso y degradación controlada. Un sistema distribuido debe asumir que la red y algunos componentes fallarán; la pregunta es qué ocurre cuando eso sucede.
Consistencia y procesamiento asíncrono
En sistemas distribuidos, mantener una transacción fuerte entre servicios es costoso. Una alternativa es delimitar la transacción crítica —por ejemplo, el cobro— y publicar eventos para tareas secundarias. Esas tareas se procesan de forma asíncrona y toleran consistencia eventual; el diseño debe aceptar duplicados, reintentos y desorden mediante identificadores idempotentes y colas.
Documentar la arquitectura
El modelo C4 propone cuatro niveles de zoom:
- Contexto: el sistema y sus usuarios o sistemas externos.
- Contenedores: aplicaciones, servicios, bases de datos y unidades desplegables.
- Componentes: piezas dentro de un contenedor y sus responsabilidades.
- Código: clases y funciones, normalmente generado o consultado bajo demanda.
El nivel de contenedores es el más útil para conversar con operación y negocio: muestra qué se despliega, con qué tecnología y cómo se comunica. No debe confundirse con el nivel de código.
Un registro de decisiones de arquitectura (ADR) documenta una decisión relevante, su contexto, las alternativas consideradas y las consecuencias. Es breve, versionado y fechado. Los diagramas sin fecha ni rationale envejecen mal; un ADR permite entender por qué el sistema llegó a su forma actual.
Ejemplo de registro de decisiones
Un ADR breve suele contener estos campos, tal como queda en este ejemplo:
| Campo | Contenido |
|---|---|
| Identificador y fecha | ADR-0014, 2026-10-23 |
| Estado | Aceptada |
| Contexto | La carga de lectura crece cada trimestre y el monolito entero se escala para atender un informe que usa una fracción de los datos |
| Decisión | Extraer el módulo de informes a un servicio propio con su base de datos de lectura y actualizarla mediante eventos |
| Alternativas | Seguir escalando el monolito; copias de base de datos para informes; consulta materializada dentro del monolito |
| Consecuencias | Latencia de red adicional, consistencia eventual en los informes, un despliegue más que observar y una tarea de reconciliación |
| Revisión | Reevaluar si la demora de los informes supera el acuerdo de servicio o si la carga deja de crecer |
El estado distingue propuesta, aceptada, obsoleta y sustituida, de modo que quien lea el registro sepa qué decisión vigente. Anotar también las alternativas descartadas evita que la misma discusión reaparezca medio año después con otro equipo. Cuando una decisión se revierte, el ADR anterior no se elimina: se marca como sustituido y se crea uno nuevo que apunta al registro previsto. Mantener el ADR junto al código que lo materializa reduce la distancia entre el razonamiento y su implementación.
Petición-respuesta frente a comunicación por eventos
| Aspecto | Petición-respuesta | Eventos |
|---|---|---|
| Acoplamiento | El emisor conoce al receptor y espera su respuesta | El emisor publica sin saber quién consumirá |
| Flujo temporal | Sincrono: la llamada ocupa una conexión mientras aguarda | Asíncrono: el emisor continúa y el consumidor procesa después |
| Fallos | Un receptor caído se nota de inmediato en el llamador | La falla se acumula en el tema o la cola hasta reintentar |
| Orden | Lo define la secuencia de llamadas del cliente | Depende de la partición, el tiempo y los reintentos |
| Observabilidad | La traza de una llamada es lineal | Exige correlacionar identificadores de evento entre servicios |
| Uso típico | Consultas, comandos con confirmación y operaciones de pago | Avisos de cambio, notificaciones y procesamiento diferido |
Los sistemas reales combinan ambos estilos: la confirmación de una reserva es síncrona, mientras la facturación y el aviso al cliente se suscriben al evento de reserva confirmada. Publicar un evento no elimina el contrato: conviene versionar el esquema del mensaje, tolerar campos nuevos y tratar el evento como parte del dominio compartido. Cuando la orquestación centralizada acumula lógica de negocio en un único componente, reaparece el acoplamiento que el desacoplamiento pretendía evitar.
Patrones de resiliencia
| Patrón | Problema que resuelve | Coste o efecto secundario |
|---|---|---|
| Tiempo de espera | Una llamada lenta retiene hilos y memoria en el cliente | Falsos positivos si el umbral es demasiado corto |
| Reintento con retroceso exponencial | Fallos transitorios de red o de un servicio vecino | Tráfico duplicado si la operación no es idempotente |
| Limitador de tasa | Un consumidor agota la capacidad compartida | Rechazos en picos mal dimensionados |
| Cortacircuitos | Seguir llamando a un servicio caído multiplica su presión | Durante la apertura, las consultas reciben respuesta alternativa |
| Aislamiento por compartimentos | Un submódulo saturado consume todos los recursos disponibles | Se duplican colas y conexiones por compartimento |
| Degradación controlada | No todos los datos tienen el mismo peso en cada pantalla | La funcionalidad queda parcial y se avisa al usuario |
| Comprobación de salud | El balanceador sigue enviando peticiones a una instancia enferma | Un umbral mal calibrado da por sano a un nodo saturado |
Toda llamada a la red debería llevar un tiempo de espera explícito; sin él, la espera indefinida es la que agota las conexiones del llamador. Los reintentos solo son seguros cuando la operación es idempotente o lleva un identificador que descarta el envío duplicado. El cortacircuitos necesita una ventana de apertura con comprobaciones periódicas para no dejar el servicio degradado de forma permanente. Estos patrones sostienen un enfoque donde se asume que los componentes fallarán y se decide de antemano qué responde el sistema en ese caso.
Caché y CDN: nociones básicas
Una caché acerca copias de datos o respuestas a donde se consumen, a cambio de aceptar que la copia pueda estar desactualizada. Los niveles habituales son el navegador, el CDN, el balanceador o la puerta de API, la aplicación y el propio motor de base de datos. Cada nivel reduce la carga del siguiente, de modo que una consulta servida en el borde evita recorrer el camino completo hasta el origen.
| Estrategia de escritura | Qué ocurre | Uso habitual |
|---|---|---|
| Escritura directa (write-through) | La escritura pasa por la caché y por el almacenamiento de origen | Datos que no admiten pérdida y necesitan lectura rápida |
| Escritura diferida (write-back) | La escritura se confirma en la caché y se aplaza al origen | Volúmenes altos con tolerancia acotada a la pérdida |
| Escritura en torno (write-around) | Solo se escribe en el origen y la caché se llena al leer | Escrituras esporádicas con lecturas repetidas |
La invalidación suele apoyarse en un tiempo de vida tras el cual la copia caduca, o en etiquetas que permiten descartar grupos de claves cuando cambia el dato. Un CDN distribuye contenido estático —imágenes, hojas de estilo, videos— desde nodos cercanos al usuario y también puede servir respuestas de API con reglas de caducidad. Los riesgos típicos son servir una copia envejecida, una tormenta de lecturas cuando muchas peticiones fallan a la vez sobre la misma clave y entregar datos de usuario desde una caché compartida entre cuentas. Por eso cada respuesta cacheable declara si es pública o privada y si admite el uso compartido.
Deuda técnica y evolución
La deuda técnica es una metáfora para las decisiones que reducen el esfuerzo hoy y aumentan el costo de cambio mañana. No todo atajo es deuda: a veces es una apuesta deliberada para validar una hipótesis antes de invertir en una solución definitiva. La diferencia está en si la decisión se registra, se mide y se paga.
Puede clasificarse en deliberada o inadvertida. La deliberada es consciente, como posponer una migración para cumplir una fecha; la inadvertida aparece cuando el equipo descubre tarde que una estructura ya no encaja. También conviene distinguir la deuda prudente, tomada con información y plan de pago, de la imprudente, que se acumula sin control.
Las señales típicas son cambios pequeños que obligan a tocar muchos módulos, tiempos de integración crecientes, incidentes que se repiten en la misma zona y pruebas difíciles de escribir. Ninguna señal por sí sola confirma deuda; el conjunto y su tendencia sí orientan.
Gestionarla no exige reescribir todo. Conviene registrar los puntos, priorizar por impacto en el negocio y reservar capacidad en cada ciclo. Para sustituir un componente, el patrón de estrangulamiento (strangler fig) introduce la nueva implementación junto a la antigua, desvía tráfico de forma gradual y retira la vieja cuando deja de usarse. Es menos arriesgado que una reescritura total y permite medir el resultado en cada paso.
Ejemplos prácticos
Caso 1: producto nuevo con equipo pequeño. Una startup construye una plataforma de reservas. El dominio cambia cada semana y el equipo tiene cuatro personas. Un monolito modular con interfaz interna clara y despliegue único permite iterar rápido; si un módulo necesita escalar por separado, puede extraerse después como servicio.
Caso 2: picos de trabajo asíncrono. Una tienda confirma pagos de forma síncrona, pero las facturas y los correos se saturan en campañas. Se mantiene la transacción de pago y se publican eventos para que trabajadores asíncronos procesen esas tareas. El cliente recibe confirmación sin esperar y el sistema escala solo a los trabajadores.
Caso 3: extracción gradual de un módulo. Un equipo mantiene un monolito de pedidos y necesita reservas de almacén en tiempo real. Extrae la reserva como servicio con su propia base de datos y publica un evento de pedido confirmado, mientras la facturación sigue resolviéndose en transacción local dentro del monolito. Durante dos semanas ambos caminos corren en paralelo y se comparan los resultados; después se retira la ruta antigua. El límite se dibujó alrededor de una capacidad de negocio, no alrededor de capas de código.
Caso 4: caché agotada en campaña. El catálogo se sirve desde el CDN, pero los precios personalizados viajan al origen en cada consulta y saturan la base de datos durante una campaña. Se separan las respuestas: el catálogo público caduca cada cinco minutos en el borde, mientras los precios de usuario se atienden desde una caché de aplicación con clave por cuenta y treinta segundos de vida. La base de datos deja de recibir el mismo patrón de lecturas repetidas.
Errores frecuentes
- Elegir microservicios por moda sin dominio estable ni capacidad operativa.
- Compartir una base de datos entre servicios y llamarlo autonomía.
- Confundir arquitectura con diseño de interfaz o con el organigrama.
- Asumir que escalar horizontalmente arregla una aplicación con estado local.
Puntos clave para el examen
- La arquitectura son decisiones estructurales con alto costo de cambio, no solo diagramas.
- Separación de responsabilidades: alta cohesión y bajo acoplamiento.
- Monolito y microservicios son compromisos; el contexto decide.
- Microservicios: despliegue independiente, contratos y datos descentralizados, con complejidad distribuida.
- Escalado horizontal añade instancias; el estado debe externalizarse.
- C4: contexto, contenedores, componentes y código.
Recursos externos
- ISO/IEC/IEEE 42010, descripción de arquitecturas: https://www.iso.org/standard/74393.html
- Martin Fowler, Microservices: https://martinfowler.com/articles/microservices.html
- The Twelve-Factor App: https://12factor.net/
- Modelo C4: https://c4model.com/
- AWS Well-Architected Framework: https://docs.aws.amazon.com/wellarchitected/latest/framework/welcome.html
Autoevaluación
-
¿Qué diferencia una decisión de arquitectura de una de diseño detallado? La primera es estructural y costosa de revertir; la segunda es local y más fácil de cambiar. La arquitectura fija límites y atributos de calidad; el diseño resuelve piezas dentro de esos límites.
-
¿Cuándo es razonable empezar con un monolito modular? Cuando el dominio se está descubriendo o el equipo es pequeño; los límites internos permiten extraer servicios después si aparece una necesidad real.
-
¿Por qué una base de datos compartida debilita la autonomía de los microservicios? Porque acopla los servicios por el esquema y las migraciones. Un cambio afecta a varios equipos y obliga a coordinar despliegues.
-
¿Qué ventaja aporta una aplicación sin estado para escalar? Permite que cualquier instancia atienda cualquier petición y que las réplicas se añadan o retiren sin perder sesión.
-
¿Qué muestra el nivel de contenedores de C4? Las unidades desplegables o ejecutables, su tecnología y sus relaciones. La estructura interna de clases corresponde al nivel de código.