Los patrones de arquitectura de software más comunes

La arquitectura de software es tanto un arte como una ciencia. Requiere experiencia, criterio técnico y una comprensión profunda de muchos conceptos. No existen soluciones mágicas que se adapten a todos los escenarios, es por ello que cada arquitectura debe diseñarse en función de los requisitos específicos de la aplicación, la organización y el equipo.

Aspectos como el rendimiento, la escalabilidad, la flexibilidad, la fiabilidad, los costes o la capacidad técnica del equipo son factores clave en la toma de decisiones.

Los patrones de arquitectura son los bloques fundamentales con los que se construye una solución. La clave está en saber cuáles aplicar en cada contexto. A continuación, comparto algunos de mis patrones favoritos, explicando en qué situaciones destacan.

1. Bounded contexts (Contextos delimitados)

Diagrama de bounded contexts en eCommerce

Aunque no es un patrón formal, el concepto de bounded context, tomado del Domain-Driven Design, es una de las herramientas más útiles para construir código fácil de mantener.

Un bounded context define una frontera lógica y coherente en la que un modelo de dominio es consistente. Esto permite gestionar la complejidad de aplicaciones grandes, manteniendo separados los conceptos de distintos dominios como catálogo, pagos, identidad, etc.

Además, estructurar el código según estos contextos ayuda enormemente si se decide migrar a una arquitectura modular o basada en microservicios.

2. Vertical slice architecture

Este enfoque consiste en dividir la aplicación en “rebanadas verticales”, agrupando toda la lógica necesaria para una funcionalidad específica (desde la UI hasta la base de datos). Es una extensión natural del enfoque de Bounded Context y promueve una alta cohesión por funcionalidad.

3. Sidecar pattern

Diagrama del patrón Sidecar

El patrón Sidecar consiste en un componente que se ejecuta junto a la aplicación principal en la misma máquina o contenedor. Se utiliza para añadir funcionalidades transversales como logging, métricas, autenticación o políticas de red, sin tocar el código de la app principal.

4. Service Mesh

Diagrama de Service Mesh

Un Service Mesh lleva el patrón Sidecar al siguiente nivel, desplegando un proxy junto a cada servicio y gestionando toda la comunicación entre ellos. Esto permite cifrar, autenticar y monitorizar el tráfico de forma centralizada y coherente.

5. Publisher-Subscriber (Pub/Sub)

Diagrama Publisher-Subscriber general

Este patrón permite una comunicación asíncrona desacoplada entre aplicaciones, mediante el uso de un intermediario (Message Broker). Es ideal para integrar servicios o ejecutar procesos de larga duración de forma no bloqueante.

En este patrón Publisher-Subscriber, los mecanismos de entrega definen cómo se distribuyen los mensajes publicados entre los consumidores (subscribers). Los dos mecanismos principales son:

Competing consumers

En este modelo, varios consumidores se suscriben a un mismo Topic o Cola, pero cada mensaje solo es entregado a uno de ellos. Es ideal cuando queremos procesar tareas pesadas o de larga duración en paralelo y escalar el sistema fácilmente.

Cada consumidor “compite” por coger el siguiente mensaje disponible, lo que permite distribuir la carga de trabajo. Por lo que se realiza un procesamiento paralelo de tareas (por ejemplo, generar PDFs, enviar emails), un balanceo de carga entre múltiples workers y escalabilidad horizontal.

Fanout

En el caso de Fanout, cada mensaje publicado se envía a todos los consumidores suscritos. Cada uno recibe una copia y la procesa de forma independiente. Es muy útil cuando varios sistemas necesitan reaccionar al mismo evento. Se trata de sincronización de datos entre servicios, que permite notificación de eventos en múltiples sistemas (por ejemplo, logs + métricas + auditoría) y que se disparan desde acciones paralelas tras un evento común.

Comparativa de mecanismos de entrega (Competing Consumers vs Fanout)

6. Application gateway

Diagrama de enrutamiento con Application Gateway

Más allá de un balanceador de carga tradicional, un Application Gateway trabaja a nivel de capa 7 (HTTP), permitiendo enrutar tráfico en función de rutas, cabeceras u otros elementos. Es perfecto para arquitecturas con múltiples servicios o productos detrás de un único dominio.

7. Microservicios

Diagrama de arquitectura de Microservicios

Dividir una aplicación en microservicios permite escalar, desplegar y mantener cada parte de forma independiente. Aunque potente, añade complejidad operativa y técnica, por lo que es recomendable solo si el proyecto realmente lo requiere.

8. Microfrontends

Diagrama de Microfrontends

Así como los microservicios dividen el backend, los Microfrontends hacen lo mismo con la interfaz web. Cada equipo puede desarrollar, desplegar y mantener su sección de UI de forma autónoma. Con Web Components es posible encapsular completamente cada parte, incluso usando diferentes frameworks.

9. CQRS (Command Query Responsibility Segregation)

Diagrama de CQRS

Este patrón separa la lectura y escritura de datos en dos modelos distintos, lo cual permite optimizar cada lado según su carga. Es útil en sistemas con necesidades de rendimiento extremo o que requieran tecnologías distintas para cada operación.

CQRS puede aplicarse con distintos grados de sofisticación, desde implementaciones simples hasta arquitecturas avanzadas con Event Sourcing. Elegir la variante adecuada depende de la complejidad de tu dominio y los requerimientos de escalabilidad, consistencia y rendimiento.

CQRS Architectures

CQRS Simple (sin separación física)

En esta variante, los comandos y las consultas usan el mismo modelo de datos, pero están separados lógicamente en el código. Es fácil de implementar y una buena opción para proyectos pequeños o medianos. Aunque debes tener en cuenta que no escala la lectura y escritura de forma independiente. La base de datos sigue siendo un cuello de botella común.

CQRS con modelos separados

Aquí se usan dos modelos distintos, uno para comandos (escrituras) y otro para consultas (lecturas), pero comparten la misma base de datos física. Esto permite optimizar consultas y comandos por separado y reducción de acoplamiento entre lógica de negocio y presentación. La limitación es que no hay aislamiento total de la carga y esto supone un riesgo de inconsistencia si no se gestiona bien la sincronización.

CQRS con bases de datos separadas

Los modelos de lectura y escritura utilizan almacenamientos de datos completamente distintos, a menudo con tecnologías optimizadas para cada caso (por ejemplo, PostgreSQL para escritura y Elasticsearch o Redis para lectura). La lectura y escritura pueden escalar de forma independiente y tiene una alta optimización por tipo de carga. Las limitaciones es que se necesita gestionar la sincronización de datos y puede introducir consistencia eventual, lo cual requiere lógica extra.

CQRS + Event Sourcing

En esta arquitectura, los comandos no modifican directamente el estado, sino que generan eventos. Estos eventos se almacenan en un event store (base de datos de solo escritura, tipo append-only) y luego se proyectan sobre uno o varios modelos de lectura. Las ventajas són que cuentan con una auditoría completa de todo lo ocurrido, permitiendo reconstruir estados y construir vistas flexibles y es ideal para dominios con alta complejidad y trazabilidad crítica. La limitación es el alto nivel de complejidad, la curva de aprendizaje pronunciada y la dificultad para depurar y testear.

Arquitectura híbrida: Integrando los patrones

Arquitectura completa eCommerce híbrida

En muchos casos, la solución óptima es combinar múltiples patrones adaptados a los requerimientos de cada contexto. Esto es lo que se conoce como arquitectura híbrida.

Un mismo sistema puede tener microservicios con bases de datos diferentes, aplicar CQRS solo donde se necesita, usar Event Sourcing en contextos críticos y facilitar la integración entre dominios con Pub/Sub o Sidecars.

Conclusión

No todos los patrones son adecuados para todos los casos. Una buena arquitectura nace del entendimiento profundo del problema, del dominio, del equipo y del contexto tecnológico. Conocer estos patrones, sus ventajas y complejidades te ayudará a tomar decisiones más acertadas y a diseñar soluciones robustas y sostenibles.