Módulo 3: Comunicar la arquitectura

4. Component y Code: cuándo bajar y cuándo parar

Descripción

Al terminar esta lección vas a conocer los dos niveles más profundos del C4 —el Component (nivel 3, el mapa del barrio) y el Code (nivel 4, el mapa de la calle)— y, sobre todo, vas a aprender la habilidad que casi nadie enseña: cuándo parar de bajar. El Component muestra qué hay dentro de un container: sus piezas de código y cómo colaboran, para el dev que va a trabajar en esa pieza. El Code muestra las clases y funciones exactas de un componente. Pero la lección más valiosa aquí no es cómo dibujarlos —es entender que el detalle no es gratis: cada nivel que bajas es otro diagrama que alguien tiene que mantener, y que se pudre si nadie lo cuida. Por eso el arquitecto experimentado dibuja Context y Container casi siempre, Component solo donde la complejidad lo amerita, y Code casi nunca a mano. La disciplina no es "documentar todo a máximo detalle"; es bajar solo donde el detalle le sirve a alguien real.

Esto importa porque el instinto del principiante es el opuesto: creer que "documentar bien" significa "documentar todo hasta el fondo". Ese instinto produce el museo de diagramas —treinta láminas Component y Code que se hicieron una vez, nadie volvió a mirar, y hoy mienten porque el código cambió y los dibujos no—. La documentación que no se mantiene es peor que no tener documentación, porque parece verdad y engaña. El arquitecto que entiende el costo del detalle produce poco y vivo en vez de mucho y muerto: dos o tres diagramas que la gente de verdad usa y que se mantienen porque son pocos, en vez de treinta que se pudren. Saber cuándo parar es tan parte del oficio como saber dibujar.

Conexión con el módulo: en la lección 3 dibujaste los dos niveles que usarás el 80% del tiempo —Context y Container—. Aquí bajas los dos peldaños restantes y, más importante, aprendes el freno: la regla del zoom que decide hasta dónde seguir. Con esto completas las cuatro C del C4 (las nombramos en la 2, las dibujamos en la 3 y la 4). En la lección 5 usarás las cuatro para el ejercicio central: elegir el nivel correcto para cada audiencia. Y la regla de "producir poco y vivo" que aprendes aquí reaparece en la lección 7, cuando ataquemos el documento de 500 páginas y el diagrama que se pudre.

Google Maps: por qué no memorizas el país calle por calle

Vuelve a Google Maps una vez más, pero fíjate ahora en algo que no haces. Cuando planeas un viaje por carretera de una punta del país a la otra, no memorizas cada calle de cada pueblo que vas a cruzar. Miras el mapa a nivel de ciudades y autopistas —el zoom que responde tu pregunta— y confías en que, cuando llegues a un pueblo concreto y necesites el detalle de sus calles, acercarás el mapa en ese momento, para ese pueblo, y ni un pueblo más. Sería absurdo imprimir el mapa calle-por-calle de los cuarenta pueblos de la ruta "por si acaso": la mayoría no los vas a necesitar, y para cuando los necesitaras, las calles quizás ya cambiaron.

Hay dos ideas ahí, y las dos son el corazón de esta lección. La primera: bajas al detalle bajo demanda, solo donde lo necesitas, no de forma preventiva en todos lados. La segunda, más profunda: el detalle envejece más rápido cuanto más fino es. La forma general del país no cambia en años; las calles de un pueblo pueden cambiar en meses; el número de una casa, cualquier día. Por eso el mapa del mundo se mantiene solo casi sin esfuerzo, y el mapa calle-por-calle exige actualización constante. En el software es idéntico: el Context de Mercado (qué hace, con quién habla) es estable por años; el Code de un componente (sus clases exactas) cambia cada semana. Dibujar y mantener a mano el nivel más fino es pelear contra un blanco que se mueve todos los días —por eso casi nunca vale la pena—.

El Component: el mapa del barrio

El Component responde: ¿qué hay dentro de este container y cómo colaboran sus partes? Haces zoom en un container —no en todos— y muestras sus componentes: agrupaciones de código con una responsabilidad clara. Dibujémoslo para el container más complejo de Mercado, el que concentra el flujo crítico del negocio: la parte de checkout dentro de la API.

C4Component
    title API de Mercado - Componentes del flujo de Checkout (nivel 3)
    Container_Boundary(api, "API (FastAPI)") {
        Component(checkout_ctrl, "Checkout Controller", "FastAPI router", "Recibe la peticion de compra")
        Component(cart, "Cart Service", "Modulo", "Arma el carrito y valida stock")
        Component(tax, "Tax Calculator", "Modulo", "Calcula impuestos por region")
        Component(pay_client, "Payment Client", "Modulo", "Habla con el gateway de pagos")
        Component(order_repo, "Order Repository", "Modulo", "Persiste el pedido")
    }
    ContainerDb(db, "Database", "PostgreSQL", "Pedidos")
    System_Ext(payments, "Payment Gateway", "Stripe")

    Rel(checkout_ctrl, cart, "Arma el carrito")
    Rel(checkout_ctrl, tax, "Pide el impuesto")
    Rel(checkout_ctrl, pay_client, "Cobra")
    Rel(checkout_ctrl, order_repo, "Guarda el pedido")
    Rel(pay_client, payments, "Cobra", "HTTPS/API")
    Rel(order_repo, db, "INSERT/UPDATE", "SQL")

Léelo como el dev que va a arreglar un bug en el cálculo de impuestos: el Checkout Controller recibe la petición y orquesta; le pide al Cart Service que arme el carrito, al Tax Calculator que calcule el impuesto, al Payment Client que cobre (y este habla con Stripe), y al Order Repository que persista el pedido (y este escribe en PostgreSQL). En treinta segundos el dev sabe dónde tocar —el Tax Calculator— y qué lo rodea, sin haber leído una línea de código. Ese es el valor del Component: orientar dentro de una pieza compleja.

Pero fíjate en la palabra compleja. Dibujamos el Component del checkout porque es el flujo crítico y enredado de Mercado —donde el pedido y el pago se abrazan, el que dio problemas en el módulo 2—. No dibujamos el Component de la web app, ni del índice de búsqueda, ni de la base de datos, porque no hacía falta: esas piezas son simples o su interior no es donde la gente se pierde. La regla del Component es selectiva: uno por container que de verdad lo amerite, no uno por container porque sí.

El Code: el mapa de la calle (y por qué casi nunca lo dibujas)

El Code responde: ¿cómo está escrito exactamente este componente? Haces zoom en un componente —digamos el Tax Calculator— y muestras sus clases, interfaces y métodos. Así se vería, como diagrama de clases:

classDiagram
    class TaxCalculator {
        +calculate(order, region) TaxResult
    }
    class TaxRule {
        <<interface>>
        +applies(region) bool
        +rate() Decimal
    }
    class RegionRule {
        +applies(region) bool
        +rate() Decimal
    }
    class ExemptRule {
        +applies(region) bool
        +rate() Decimal
    }
    class TaxResult {
        +amount Decimal
        +breakdown list
    }
    TaxCalculator --> TaxRule : usa
    TaxRule <|.. RegionRule : implementa
    TaxRule <|.. ExemptRule : implementa
    TaxCalculator --> TaxResult : produce

Es un diagrama correcto y detallado. Y aquí viene la lección: casi nunca vas a dibujar esto a mano. Tres razones. Primera, se pudre en horas: en cuanto alguien añada una TieredRule o renombre un método, el diagrama miente, y nadie va a acordarse de actualizar el dibujo cada vez que toca el código. Segunda, el código real es mejor documentación de sí mismo: para entender exactamente cómo está escrito el TaxCalculator, abrir el archivo en el IDE —con autocompletado, navegación y la versión de hoy— le gana a cualquier diagrama de clases dibujado a mano el mes pasado. Tercera, si de verdad necesitas la vista visual, la generas desde el código con una herramienta (muchos IDEs y librerías producen diagramas de clases automáticamente), de modo que se regenera actualizada cuando la necesitas y no hay nada que mantener.

Por eso el C4 incluye el nivel Code por completitud, pero Simon Brown mismo recomienda no dibujarlo salvo casos muy puntuales —una pieza algorítmica delicada que valga la pena explicar en detalle—, y aun ahí, preferir generarlo. El trabajo de comunicación del arquitecto vive arriba, en Context y Container; el nivel Code es territorio del dev y del IDE, no de la lámina mantenida a mano.

Ejemplo trabajado: cuántos diagramas de verdad mantienes

Juntemos la regla del zoom en números. La pregunta práctica no es "¿cuántos niveles tiene el C4?" (cuatro), sino "¿cuántos diagramas de verdad vale la pena mantener en un sistema como Mercado?". El siguiente código cuenta el inventario razonable de diagramas por nivel y muestra que la respuesta es tres, no treinta —porque bajas solo donde el detalle sirve—.

# Bajar de nivel cuesta: cada zoom que abres es otro diagrama que mantener.
# Por eso casi nadie llega a Code, y casi nadie dibuja TODOS los componentes.

# Mercado, contado por nivel (piezas reales del caso):
inventory = {
    "Context":   {"diagrams": 1, "note": "un solo diagrama para todo el sistema"},
    "Container": {"diagrams": 1, "note": "un solo diagrama para todos los containers"},
    "Component": {"diagrams": 1, "note": "solo el container complejo: el API de checkout"},
    "Code":      {"diagrams": 0, "note": "se genera del codigo cuando hace falta, no se dibuja a mano"},
}

print("Cuantos diagramas mantendrias en Mercado, por nivel:")
print()
print(f"{'Nivel':<11}{'Diagramas':<11}Comentario")
print("-" * 72)
maintained = 0
for level, info in inventory.items():
    maintained += info["diagrams"]
    print(f"{level:<11}{info['diagrams']:<11}{info['note']}")
print("-" * 72)
print(f"{'Total':<11}{maintained:<11}diagramas dibujados a mano (se pudren si no los cuidas)")
print()

# La regla del zoom: baja SOLO donde la complejidad lo pide.
print("Regla del zoom: baja un nivel solo donde el detalle ayuda a alguien a trabajar.")
print("  Context + Container: casi siempre valen la pena (2 diagramas).")
print("  Component: solo en los containers complejos (aqui: 1, el checkout).")
print("  Code: casi nunca a mano; lo genera el IDE del codigo real y no envejece.")

Qué esperar. Al correrlo:

Cuantos diagramas mantendrias en Mercado, por nivel:

Nivel      Diagramas  Comentario
------------------------------------------------------------------------
Context    1          un solo diagrama para todo el sistema
Container  1          un solo diagrama para todos los containers
Component  1          solo el container complejo: el API de checkout
Code       0          se genera del codigo cuando hace falta, no se dibuja a mano
------------------------------------------------------------------------
Total      3          diagramas dibujados a mano (se pudren si no los cuidas)

Regla del zoom: baja un nivel solo donde el detalle ayuda a alguien a trabajar.
  Context + Container: casi siempre valen la pena (2 diagramas).
  Component: solo en los containers complejos (aqui: 1, el checkout).
  Code: casi nunca a mano; lo genera el IDE del codigo real y no envejece.

Tres. Ese es el número que separa la documentación viva de la muerta. Compara: si hubieras dibujado un Component de los cinco containers y un Code de cada componente, tendrías fácilmente treinta láminas. Nadie mantiene treinta láminas —se pudren todas, y una documentación que miente es peor que ninguna—. En cambio, tres diagramas se mantienen: son pocos, se revisan en la misma revisión de código, y la gente los usa porque confía en que están al día. La regla del zoom no es pereza; es la condición para que la documentación siga siendo verdad. Menos diagramas, mejor mantenidos, más usados.

La regla del zoom, dicha en una frase

Toda esta lección cabe en una regla: baja un nivel solo donde el detalle le sirve a alguien real para trabajar, y para en cuanto deja de servir. Aplicada:

  • Context: casi siempre. Todo sistema merece su mapa del mundo; es barato, estable y lo usa mucha gente.
  • Container: casi siempre. Todo sistema con más de una pieza merece su mapa de la ciudad; es la herramienta de orientación técnica por excelencia.
  • Component: a veces, selectivo. Solo los containers complejos —donde la gente se pierde por dentro— merecen su mapa del barrio. En Mercado, el checkout sí; la web app no.
  • Code: casi nunca a mano. El mapa de la calle envejece demasiado rápido; cuando de verdad lo necesitas, abres el código o lo generas. Dibujarlo a mano es la excepción rarísima, reservada a un algoritmo delicado que valga oro explicar.

El criterio en cada peldaño es el mismo: ¿hay una persona concreta con una tarea concreta a quien este nivel de detalle le ahorra tiempo? Si sí, baja. Si estás bajando "para que esté completo" o "por si acaso", para —estás fabricando museo—.

Generar el Code en vez de dibujarlo: por qué eso lo mantiene vivo

Dijimos que el Code casi nunca se dibuja a mano y que, cuando de verdad lo necesitas, se genera. Vale la pena entender qué significa eso en concreto, porque es el mismo principio que en la lección 7 llamaremos docs-as-code, aplicado al nivel más fino.

"Generar" un diagrama de Code quiere decir que una herramienta lo produce a partir del código real, no de tu memoria ni de un dibujo que hiciste el mes pasado. Muchos IDEs generan un diagrama de clases de un paquete con un par de clics; herramientas como las que acompañan al C4 (por ejemplo, las que dibujan a partir de una descripción textual del sistema) pueden regenerar la vista cada vez que la pides. La diferencia con dibujar a mano es total: el diagrama generado refleja la versión de hoy porque se produce de hoy, y si el código cambia mañana, mañana lo regeneras actualizado. No hay nada que "mantener" —no existe un artefacto separado que pueda desincronizarse—; existe el código, que es la fuente de verdad, y una vista que se destila de él bajo demanda.

Esto cambia la economía del nivel Code por completo. Un diagrama de clases dibujado a mano tiene un costo de creación (una vez) más un costo de mantenimiento infinito y creciente (actualizarlo con cada cambio, para siempre, algo que nadie sostiene). Un diagrama de clases generado tiene costo de creación cero (lo produce la herramienta) y costo de mantenimiento cero (no se mantiene, se regenera). Por eso la recomendación no es "no mires nunca las clases" —a veces necesitas esa vista— sino "no la dibujes ni la guardes a mano; genérala cuando la necesites y déjala morir cuando termines".

El mismo razonamiento explica por qué, aun para el Component (nivel 3), muchos equipos prefieren describir el diagrama como texto versionado (mermaid, PlantUML C4) junto al código, en vez de una imagen exportada. Un diagrama-como-texto vive en el repositorio, cambia en el mismo commit que el código que describe, y se revisa en el mismo pull request —así que envejece mucho más despacio que una imagen que alguien dibujó y subió a una wiki—. La regla general que emerge: cuanto más fino es el nivel (más cerca del código que cambia a diario), más conviene que el diagrama se genere o se versione con el código en vez de dibujarse y guardarse aparte. El Context sobrevive dibujado a mano porque casi no cambia; el Code no, porque cambia todos los días.

Errores comunes

Dibujar el Code a mano y jurar mantenerlo (de optimismo). Qué pasa: el equipo hace un hermoso diagrama de clases del componente estrella, lo pone en el wiki, y promete actualizarlo con cada cambio. Tres semanas después el código cambió cinco veces y el diagrama ninguna; ahora miente. Por qué pasa: en el momento de dibujarlo, mantenerlo parece fácil; nadie proyecta el costo acumulado de actualizarlo cada vez. Cómo detectarlo: si tienes un diagrama de clases hecho a mano de hace más de un mes, casi seguro ya no coincide con el código. Cómo corregirlo: no dibujes Code a mano; si necesitas la vista, genérala desde el código en el momento, o simplemente abre el código —es la fuente de verdad y siempre está al día—.

Un Component de cada container (de simetría). Qué pasa: "hicimos el Component del checkout, hagamos el de todos para que quede parejo". Resultado: cinco Component, cuatro de los cuales muestran piezas simples que nadie necesitaba desglosar, y ahora hay cinco diagramas que mantener en vez de uno. Por qué pasa: la simetría se siente ordenada ("todos los containers documentados igual"). Cómo detectarlo: si dibujaste el Component de un container y no puedes nombrar a la persona concreta que lo va a usar, no debiste dibujarlo. Cómo corregirlo: Component es selectivo por diseño —uno donde la complejidad se lo gana, cero donde no—; la asimetría (checkout sí, web app no) es correcta, no un descuido.

Confundir "completo" con "útil" (de exhaustividad). Qué pasa: el equipo se enorgullece de tener "los cuatro niveles documentados de todo el sistema" —treinta láminas— y se sorprende de que nadie las use y de que estén desactualizadas. Por qué pasa: se equipara la cantidad de documentación con su calidad; "más completo" suena a "mejor". Cómo detectarlo: mide cuántos de tus diagramas se consultaron o actualizaron el último trimestre; si la mayoría no, tienes museo, no comunicación. Cómo corregirlo: la meta no es cubrir todos los niveles de todo, es que cada diagrama que existe le sirva a alguien y se mantenga vivo; tres diagramas usados vencen a treinta ignorados.

Ejercicios

Ejercicio 1 — ¿Component sí o no? Para cada container de un sistema, decide si vale la pena dibujarle un Component y justifica: (a) una base de datos PostgreSQL; (b) una API que orquesta un flujo de reservas con validación de disponibilidad, cálculo de precio dinámico, aplicación de cupones y notificaciones; (c) una app web React que solo consume la API y renderiza; (d) un worker que lee una cola y envía correos.

Ver solución

(a) La base de datos → No. Una PostgreSQL no tiene "componentes internos" que dibujar en el sentido del C4 —su estructura relevante es el esquema, que se documenta de otra forma (un ERD), no con un Component—. Nada que ganar bajando.

(b) La API con flujo complejo → Sí. Aquí conviven varias responsabilidades enredadas (disponibilidad, precio dinámico, cupones, notificaciones) y un dev que entra fácil se pierde. Un Component que muestre esos módulos y cómo colaboran ahorra horas de leer código a ciegas. Es el caso clásico de "container complejo que amerita zoom".

(c) La app web que solo consume y renderiza → Probablemente no. Si es un front-end estándar que llama a la API y pinta pantallas, su estructura interna es predecible y un Component no aporta. Excepción: si tuviera lógica de cliente muy elaborada (un editor colaborativo, un motor de reglas en el navegador), entonces sí.

(d) El worker de correos → No. Es simple —lee cola, envía correo—; su comportamiento se entiende del Container y su nombre. Bajar a Component no le ahorra tiempo a nadie.

La moraleja: el criterio no es el tipo de pieza, es si alguien se pierde por dentro. La API compleja (b) sí; las tres simples, no. Component selectivo, no simétrico.

Ejercicio 2 — El diagrama de clases que envejeció. Un equipo tiene, en su wiki, un diagrama de clases del módulo de precios hecho hace ocho meses. Un dev nuevo lo usa para entender el código, pero el diagrama muestra tres clases que ya no existen (se refactorizaron) y omite dos nuevas. El dev pierde medio día confundido. ¿Qué falló en la decisión original de crear ese diagrama, y qué debieron hacer?

Ver solución

Lo que falló no fue dibujar mal el diagrama —en su momento era correcto—; falló la decisión de mantener a mano un artefacto del nivel Code, que es exactamente el nivel que envejece más rápido. Un diagrama de clases dibujado a mano tiene fecha de caducidad de días o semanas, porque el código a esa escala cambia constantemente y nadie recuerda (ni debería tener que recordar) actualizar el dibujo con cada refactor. El diagrama no solo se volvió inútil: se volvió activamente dañino, porque parecía autoridad y le costó medio día al dev. Un diagrama que miente es peor que no tener diagrama, porque el dev habría leído el código directo y no se habría confundido.

Qué debieron hacer: no mantener un diagrama de clases a mano. Opciones correctas: (1) para entender el módulo de precios, dejar que el dev lea el código real —siempre está al día— con quizás un README corto que explique el propósito y el porqué (que sí es estable); (2) si de verdad querían una vista visual de clases, generarla desde el código con una herramienta cuando se necesite, para que refleje la versión de hoy; (3) documentar el nivel Component (más estable) en vez del Code, si lo que buscaban era orientar. La regla: lo que cambia todos los días no se dibuja a mano; se lee de la fuente o se genera.

Ejercicio 3 — El presupuesto de mantenimiento. Tu equipo tiene tiempo para mantener bien tres diagramas de un sistema de mediana complejidad (una app, una API con dos flujos complejos, una base de datos, un worker). Si tuvieras que elegir exactamente tres diagramas para que la documentación sea máximamente útil y sostenible, ¿cuáles elegirías y por qué dejarías fuera el resto?

Ver solución

Los tres: (1) el Context, (2) el Container, y (3) un Component del flujo más complejo de la API.

  • Context (1): barato de mantener (cambia poco), sirve al negocio y a cualquier recién llegado para entender qué es el sistema. Irrenunciable.
  • Container (2): el mapa técnico de orientación; todo dev que entra lo necesita para saber dónde vive cada cosa. Cambia poco (las piezas desplegables son estables). Irrenunciable.
  • Un Component del flujo complejo (3): de los dos flujos complejos de la API, eliges el que más gente toca o el más enredado; ese Component ahorra horas de perderse. Es el único nivel 3 que amerita el gasto.

Qué dejas fuera y por qué: el Component del segundo flujo (si es menos crítico, vive sin él; la gente puede leer el código con el Container como mapa); cualquier diagrama de Code (se pudre demasiado rápido; se genera o se lee del código); y Component de las piezas simples (app, worker, base de datos), que no lo ameritan. Con tres diagramas vivos —dos estables y uno crítico— cubres las dos audiencias (negocio y devs) y el punto donde la gente se pierde, y todo se mantiene porque son pocos. Es exactamente el resultado que midió el ejemplo trabajado: tres, no treinta.

Resumen y siguiente paso

En esta lección completaste las cuatro C: dibujaste el Component del checkout de Mercado (el mapa del barrio, para el dev que trabaja dentro de esa pieza compleja) y viste el Code (el mapa de la calle) para entender por qué casi nunca se dibuja a mano —se pudre en horas, el código es mejor fuente de sí mismo, y si acaso se genera—. Sobre todo, aprendiste la regla del zoom: bajas un nivel solo donde el detalle le sirve a una persona real para trabajar, y paras en cuanto deja de servir. Lo mediste: en Mercado, la documentación viva son tres diagramas (Context, Container, un Component), no treinta —porque menos diagramas, mejor mantenidos, se usan; treinta se pudren—.

Antes de avanzar deberías poder: dibujar un Component de un container complejo y decidir, container por container, si amerita uno; explicar por qué el Code casi nunca se dibuja a mano; y aplicar la regla del zoom para elegir el pequeño conjunto de diagramas que de verdad conviene mantener.

Lo que sigue es el corazón del módulo. Con las cuatro herramientas dibujadas, la lección 5 responde la pregunta que las gobierna a todas: ¿cuál le doy a quién? Vas a construir, ejecutado, un recomendador que empareja cada audiencia con su nivel de zoom, y un detector de desajuste que mide qué pasa cuando alguien recibe un nivel de más (se ahoga en detalle) o de menos (no puede trabajar). Es el paso de "sé dibujar los cuatro niveles" a "sé elegir el correcto para la persona que tengo enfrente" —que es, al final, todo el oficio de comunicar—.

Recursos

  • Simon Brown — Component diagram (c4model.com) — la definición canónica del nivel 3 y, clave, la recomendación de dibujarlo solo donde aporta. Contrástala con tu Component del checkout.
  • Simon Brown — Code diagram (c4model.com) — la definición del nivel 4 y por qué su propio autor recomienda no dibujarlo salvo casos puntuales, y preferir generarlo. La fuente directa de la sección "por qué casi nunca lo dibujas".
  • Simon Brown — "Diagrams as code" — herramientas (Structurizr, PlantUML C4) que permiten mantener y generar diagramas desde texto o código, el antídoto contra el diagrama que se pudre. Adelanta el docs-as-code de la lección 7.
  • Martin Fowler — Software Architecture Guide — para el criterio de fondo: qué vale la pena documentar y qué no. Ayuda a interiorizar "producir poco y vivo" como postura, no solo como truco.