Curso exprés · No. 03

Rara vez inventás una arquitectura. Reconocés qué forma conocida encaja con el problema que tenés delante — y pagás el precio que esa forma cobra.

Solo la esencia · Una imagen por patrón · Ejemplos antes que teoría

§ 01

Un patrón es una respuesta reutilizable a un problema que vuelve una y otra vez. Conocer el catálogo significa que dejás de reinventar — y empezás a elegir.

Un patrón es una respuesta con nombre, no una regla

En el ajedrez no reinventás la apertura en cada partida. Reconocés «esto es una Siciliana» y ya sabés hacia dónde tiende a llevar.

Un patrón de arquitectura es lo mismo: un problema que se repite en miles de sistemas — ¿cómo se encuentran las partes entre sí? ¿cómo sobrevive a la carga? — emparejado con una forma conocida que funciona y costos conocidos. No estás siendo ingenioso. Estás parado sobre décadas de cicatrices ajenas. Cuando decís «los servicios necesitan reaccionar a cosas que pasan en otro lado», eso es orientado a eventos — ya resuelto, con nombre y documentado.

Los patrones de arquitectura no son patrones de diseño

Un patrón de diseño es cómo acomodás los muebles en una habitación. Un patrón de arquitectura es el plano de toda la casa.

Los patrones de diseño (Factory, Observer, Strategy) viven dentro de un módulo, en el código. Los patrones de arquitectura deciden las cajas grandes y las líneas entre ellas — qué se despliega por separado, qué le habla a qué, dónde viven los datos. Este curso es sobre los planos, no sobre los muebles.

Todo patrón cobra un precio

Un auto deportivo y una minivan son los dos «correctos» — para vidas distintas. Ninguno es el mejor auto en abstracto.

No existe el mejor patrón, solo un encaje. Los microservicios te compran escalado independiente y te cobran un sistema distribuido para operar. Un monolith en capas te compra simplicidad y te cobra un escalado doloroso más adelante. La habilidad está en leer la factura antes de firmarla.

No elegís el patrón famoso. Elegís aquel cuyo precio podés pagar.

§ 02

La mayoría de los sistemas empiezan como un único deployable. Los primeros patrones son sobre cómo acomodás lo de adentro para que no se convierta en barro.

En capas (n-tier): apilar responsabilidades en pisos

Un restaurante: el salón toma los pedidos, la cocina cocina, la despensa almacena. El mozo nunca cocina; el chef nunca baja al sótano — lo hace la capa de abajo.

El patrón más común de todos. El código se parte en capas horizontales — presentación, lógica de negocio, acceso a datos, base de datos — y una request cae derecho a través de ellas. Ejemplo: una app web donde un controller llama a un service, que llama a un repository, que pega contra Postgres. Es fácil de entender y fácil de testear (mockeás la capa de abajo). El precio: todo se despliega como una sola unidad, y un cambio trivial igual puede significar redesplegar todo.

Monolith modular: un edificio, con departamentos bien amurallados

Un edificio de departamentos — un solo techo, una sola entrada, pero cada departamento tiene sus propias paredes y su propia puerta. No atravesás el dormitorio del vecino para llegar a la escalera.

Sigue siendo un único deployable, pero lo de adentro está partido en módulos — facturación, catálogo, cuentas — que hablan solo a través de interfaces claras, nunca metiendo la mano en las tablas del otro. Ejemplo: un backend donde catalogue nunca importa los internals de billing, solo sus funciones públicas. Conseguís casi toda la claridad de los microservicios sin nada de la red. Para casi todo el mundo, este es el default correcto.

MVC / MVVM: mantené la pantalla lejos del cerebro

Un teatro: el guion, los actores y el director, y el escenario con las luces son trabajos distintos hechos por personas distintas. Podés cambiar la escenografía sin reescribir la obra.

Estos patrones ponen una pared entre la interfaz de usuario y la lógica de negocio y los datos, para que un cambio en cómo algo se ve no pueda poner en peligro lo que hace. Ejemplo: Django y Rails usan MVC (el Model son los datos, la View es la página, el Controller es el pegamento); las apps web ricas y móviles se inclinan a MVVM, donde un «view model» guarda el estado de pantalla que la vista solo refleja. La pared es siempre la misma: presentación de un lado, significado del otro.

Un monolith no es un quilombo. Un monolith sin paredes internas, sí.

§ 03

La misma idea dibujada de tres maneras: empujá el framework, la base de datos y la web hacia los bordes, y mantené las reglas de negocio ignorantes de todo eso.

Ports and adapters (hexagonal): enchufes estándar alrededor de un núcleo

Una consola de juegos tiene puertos estándar. El juego no sabe si enchufaste un joystick, un volante o un palo de arcade — solo lee «input».

La lógica de negocio se sienta en el medio y define ports — interfaces como «guardar un pedido» o «enviar una notificación». Los adapters del lado de afuera los implementan para una tecnología específica: Postgres, Stripe, email. Ejemplo: tu dominio llama a un port OrderRepository; un adapter lo respalda con Postgres en producción y otro con una lista en memoria en los tests. El mundo exterior se puede cambiar sin que el núcleo se entere jamás.

Clean / Onion: las dependencias apuntan hacia adentro

Una cebolla: el núcleo en el centro, capas envueltas alrededor. Podés pelar la piel exterior sin dañar el corazón — y el corazón no depende de la piel.

El mismo principio, dibujado como círculos concéntricos: entidades y casos de uso en el centro, frameworks e I/O en el borde, y cada flecha de dependencia apunta hacia el centro. Ejemplo: la regla «una factura por más de 10k necesita aprobación» vive en una clase plana que no importa nada de tu framework web ni de tu ORM — así sobrevive a ambos. Podés reemplazar la librería de UI, cambiar el framework web, migrar la base de datos, y las reglas sobreviven intactas.

Los frameworks son detalles. Tratalos como el empapelado, no como los cimientos.

§ 04

A cierta escala — de carga, o de equipos — una sola caja deja de funcionar. Estos patrones cortan el sistema en piezas que se despliegan de manera independiente, y a cambio te entregan una red.

Microservicios: un patio de comidas, no una gran cocina única

Un patio de comidas — locales separados, cada uno con su propia cocina, su menú y su caja. El local de pizza se llena y contrata dos cocineros más; el de ensaladas no tiene por qué hacerlo.

La app se parte en servicios chicos, cada uno dueño de una capacidad y de sus propios datos, cada uno desplegado y escalado por su cuenta. Ejemplo: servicios separados de pedidos, inventario y envíos hablando por HTTP o por un message bus; en Black Friday escalás solo «pedidos» y «pagos». Son el descendiente flaco de SOA, que intentó la misma partición pero ruteaba todo a través de un pesado enterprise service bus único y se ahogó con él. El precio es alto y real: latencia de red, sin transacciones fáciles entre servicios, y un sistema distribuido para correr. Correcto para equipos grandes y carga despareja — exagerado para una startup de cinco personas.

Serverless / functions: alquilá la cocina por plato

En lugar de tener un auto que no podés mantener lleno, llamás un taxi por viaje y pagás solo mientras corre el taxímetro.

Escribís funciones chicas y la nube las corre on demand, escalando de cero a miles y de vuelta, facturando por ejecución. Ejemplo: una imagen subida al storage dispara una función que hace thumbnails — sin un servidor ocioso entre subida y subida. Genial para trabajo con picos, con forma de evento; incómodo para tareas largas y cualquier cosa que no tolere un cold start.

Los sistemas distribuidos convierten llamadas a funciones en llamadas de red. Cada una de ellas puede fallar.

§ 05

Una vez que tenés más de una caja, la arquitectura está sobre todo en las líneas entre ellas. Cómo hablan decide cómo fallan.

Request–response: pedí y esperá en el mostrador

Pedir en un mostrador — preguntás, te quedás parado ahí, y recibís tu café antes de seguir.

La forma por defecto: un cliente llama a un servidor y espera la respuesta (REST, gRPC, una query a la base de datos). Simple y directo. La trampa es el acoplamiento en el tiempo — si el servidor está lento o caído, el que llama también queda trabado esperando. Ejemplo: un checkout llamando a la API de pagos y bloqueándose hasta que responda.

Orientado a eventos: anunciá y dejá que los oyentes reaccionen

Una redacción. Algo pasa, sale el anuncio, y quien le interese — la mesa de deportes, el clima, los avisos — reacciona por su cuenta. El que anuncia no espera a nadie.

En lugar de llamar a los servicios directamente, un componente emite un evento — «OrderPlaced» — y cualquier interesado reacciona. Dos sabores: broker (los eventos se encadenan libremente, sin director) y mediator (un coordinador corre los pasos en orden). Ejemplo: «OrderPlaced» se abre hacia email, inventario y analytics — cada uno independiente, cada uno escalable, ninguno bloqueando el pedido. El costo: el flujo es más difícil de seguir y de testear, y heredás duplicados y problemas de orden.

Colas y pub/sub: un correo entre ellos

Una oficina de correo guarda tu carta hasta que el destinatario esté listo. La dejás y te vas; ellos la leen cuando se despiertan.

Una cola de mensajes o un broker pub/sub (RabbitMQ, Kafka) se sienta entre emisor y receptor para que ya no tengan que estar online en el mismo momento. Ejemplo: una request de reporte lenta se deja en una cola, un worker la levanta cuando está libre, y el sitio web sigue ágil. Esto compra resiliencia y suaviza los picos — al costo de resultados eventuales, no inmediatos.

Pipe-and-filter: una línea de montaje para datos

Una línea de embotellado — lavar, llenar, tapar, etiquetar — cada estación hace una cosa y pasa la botella. O agua a través de una pila de filtros.

Los datos fluyen a través de una cadena de pasos independientes, cada uno transformándolos y pasándolos al siguiente. Ejemplo: un pipe de Unix (grep | sort | uniq), un job de ETL, o un compilador (tokenizar, parsear, optimizar, emitir). Cada filtro es simple, testeable y reordenable. Lo mejor cuando el trabajo es una secuencia clara de transformaciones — equivocado cuando los pasos necesitan volver atrás y chusmear entre ellos.

Síncrono es una llamada telefónica. Asíncrono es una carta. Elegí según cuánto debería depender cada lado de que el otro esté despierto.

§ 06

Algunos patrones no son sobre cajas en absoluto. Son sobre cómo escribís, leés y recordás la verdad.

CQRS: separá la manera en que escribís de la manera en que leés

Una biblioteca tiene un catálogo rápido para encontrar libros, y una trastienda donde los libros se archivan y reacomodan de verdad. Buscar está optimizado de una manera; almacenar, de otra.

CQRS parte el write model (comandos que cambian cosas) del read model (queries que las traen), para que cada uno pueda darse forma y escalarse para su propio trabajo. Ejemplo: un dashboard lee de una vista desnormalizada y precalculada mientras las escrituras van a un store transaccional limpio. Poderoso cuando las lecturas superan ampliamente a las escrituras — pero son dos modelos que mantener en sync, así que no eches mano de él por defecto.

Event sourcing: guardá la historia, derivá el presente

Un banco no guarda solo tu saldo — guarda cada depósito y cada extracción. El saldo es solo la suma. Si lo perdés, lo podés recalcular desde el libro mayor.

En lugar de guardar el estado actual y sobrescribirlo, guardás el stream completo de los eventos que pasaron; el estado actual es un replay de ellos. Ejemplo: una cuenta llevada como «abierta, +100, −30» en vez de «saldo: 70» — dándote un audit trail perfecto y la capacidad de preguntar «¿cómo se veía esto el martes pasado?». El precio: consultar el "ahora" es más difícil (a menudo emparejado con CQRS), y los eventos son para siempre — no podés simplemente editar la historia.

Saga: un trato largo que podés deshacer

Reservar un viaje — vuelo, hotel, auto. Si el auto se cae, cancelás el hotel y el vuelo también. No hay un único «deshacer»; vas desandando cada paso.

Cuando una acción de negocio abarca varios servicios que cada uno es dueño de su base de datos, no la podés envolver en una sola transacción. Una saga encadena transacciones locales y, ante una falla, corre acciones compensatorias para deshacer las anteriores. Ejemplo: crear pedido, reservar stock, cobrar tarjeta; si el cobro falla, liberás el stock y cancelás el pedido. Es cómo mantenés consistentes los datos distribuidos sin un lock global.

Una transacción es un átomo de intención. Cuando abarca varios servicios, el átomo lo tenés que construir vos.

§ 07

Unos pocos patrones existen para un dolor específico. Echá mano de ellos cuando tengas exactamente ese dolor — y ni un momento antes.

Microkernel / plug-in: un núcleo diminuto con partes intercambiables

Un taladro: un solo motor, una docena de puntas intercambiables. El taladro queda igual; le enganchás lo que el trabajo necesite.

Un núcleo mínimo provee lo básico, y todo lo demás llega como plug-ins que se registran por su cuenta sin depender unos de otros. Ejemplo: VS Code, Eclipse y los navegadores son sobre todo hosts de plug-ins; un sistema de seguros puede mantener las reglas específicas de cada estado como plug-ins, así un estado nuevo es un módulo nuevo en vez de una reescritura. Ideal cuando las features son volátiles o específicas de cada cliente.

Space-based: copiá los datos en memoria, matá el cuello de botella

Un estadio con veinte boleterías, cada una con su propia copia del mapa de butacas en la mano — en lugar de una sola taquilla donde todos hacen cola.

Cuando una sola base de datos se vuelve el cuello de botella bajo carga extrema, este patrón reparte muchas unidades de procesamiento que cada una mantiene los datos en memoria, sincronizados entre sí — sin una base de datos central donde hacer cola. Ejemplo: una venta flash o un remate recibiendo una marea de pujas concurrentes. Es poderoso y caro, y solo se gana su lugar con una concurrencia genuinamente enorme y con picos.

Peer-to-peer: ningún centro en absoluto

Una comida a la canasta — cada uno es a la vez cocinero e invitado. Sin restaurante, sin mozo; el grupo se alimenta a sí mismo.

Los nodos hablan directamente entre sí sin servidor central, cada uno a la vez cliente y proveedor. Ejemplo: BitTorrent, donde todo el que descarga también sube, así el sistema se vuelve más fuerte cuanta más gente se suma. Maravilloso para resiliencia y escala sin un host central — difícil para la consistencia y el control.

Los patrones exóticos son medicina, no vitaminas. Tomalos para una enfermedad diagnosticada.

§ 08

Los patrones son un vocabulario, no un menú del que pedís un solo plato. Los sistemas reales combinan varios en capas — y la jugada más sabia suele ser la más simple.

Combinás patrones; rara vez elegís uno solo

Una casa usa muchos patrones a la vez — muros de carga, plomería, cableado, aislación — no «el único patrón de construcción verdadero».

Un sistema real podría ser un monolith modular por dentro, clean architecture en cada módulo, request-response para la UI, un evento para los side-effects lentos, y una cola delante del job pesado. La pregunta nunca es «qué patrón» sino «qué patrones, dónde». Ejemplo: la mayoría de los productos exitosos son un aburrido monolith en capas al que le crecieron un puñado de eventos y uno o dos servicios extraídos justo donde estaba el dolor.

Empezá con la forma más simple que funcione

No colás los cimientos de un estadio para construir una cabaña.

Casi todo sistema debería empezar como un monolith modular y ganarse su complejidad. Microservicios, CQRS, event sourcing, grillas space-based — estas son respuestas a problemas de escala y tamaño de equipo que quizás nunca tengas. Adoptalos cuando el dolor sea real y tenga nombre, no por anticipación. El sistema distribuido más barato es el que no construiste.

Antes de elegir un patrón
  • ¿Qué problema recurrente resuelve esta forma? Nombralo en una sola frase. - ¿Tengo ese problema ahora — o estoy adivinando sobre el futuro? - ¿Qué cuesta? En latencia, ops, complejidad, plata. - ¿Cuál es el patrón más simple que igual funcionaría? Empezá ahí. - ¿Puedo adoptar esto más adelante, cuando el dolor realmente aparezca? - ¿Con qué patrones va a tener que convivir este? Tienen que encajar entre sí.
Olores de que sobre-ingenierizaste
  • Tenés más servicios que ingenieros. - Una sola acción de usuario toca cinco servicios y dos colas para hacer lo que podría una llamada a una función. - Agregaste CQRS o event sourcing antes de tener un problema de lectura o de auditoría. - Nadie en el equipo puede dibujar el sistema entero de memoria. - Elegiste el patrón porque es lo que usan las empresas grandes.
Señales de que elegiste bien
  • Un ingeniero nuevo puede encontrar dónde va un cambio en minutos. - Las partes que cambian juntas viven juntas; las partes que escalan distinto están separadas. - Cuando algo se rompe, el radio de daño es una caja, no todas. - Podés explicar el trabajo y el precio de cada patrón en una frase. - La arquitectura coincide con la carga y el equipo que realmente tenés, no el que imaginás.

La mejor arquitectura es la más simple que sobrevive a tus requisitos reales — y ni un patrón más.

Fin del curso exprés · 8 capítulos · patrones antes que teoría

Lo que sigue es práctica. Cada patrón de acá se despliega en libros y cicatrices de batalla: «Software Architecture Patterns» de Mark Richards, «Fundamentals of Software Architecture», «Designing Data-Intensive Applications», «Building Microservices». Pero antes de adoptar cualquiera de ellos — nombrá el problema que tenés. El patrón es la respuesta; el problema es la pregunta que lo gana.