Módulo 3: Comunicar la arquitectura

3. Context y Container: los dos mapas que casi siempre necesitas

Descripción

Al terminar esta lección vas a saber dibujar los dos niveles del C4 que usarás el 80% del tiempo: el Context (nivel 1, el mapa del mundo) y el Container (nivel 2, el mapa de la ciudad). No es casualidad que sean los dos primeros: entre ambos cubren casi toda la necesidad real de comunicar una arquitectura. El Context le dice a cualquiera —incluido el negocio— qué hace el sistema y con quién habla; el Container le dice a cualquier técnico de qué piezas desplegables se compone y cómo se conectan. Los niveles 3 y 4 (Component, Code) son para casos concretos y acotados; los niveles 1 y 2 son el pan de cada día. Si de todo este módulo solo te llevaras la habilidad de dibujar un buen Context y un buen Container de un sistema, ya serías mejor comunicando arquitectura que la mayoría.

Esto importa porque estos dos diagramas resuelven las dos conversaciones más frecuentes del arquitecto. La primera es con el negocio: el VP que quiere saber dónde encaja una idea nueva, el ejecutivo que aprueba un presupuesto, el cliente que evalúa una integración. Esa conversación se gana con un Context —cinco cajas, cero jerga, la foto del sistema en su mundo—. La segunda es con los desarrolladores: el dev nuevo que necesita orientarse, el equipo de ops que despliega, el ingeniero de otro equipo que va a integrar. Esa se gana con un Container —las piezas reales, sus tecnologías, sus conexiones—. Dominar estos dos mapas es dominar las dos audiencias que un arquitecto enfrenta casi todos los días.

Conexión con el módulo: en la lección 2 construiste la escalera del C4 —los cuatro niveles nombrados—. Aquí bajas por los dos primeros peldaños y los dibujas a fondo sobre Mercado. Esta lección es puramente práctica: vas a ver el Context y el Container de Mercado en ```mermaid, entender qué entra en cada uno y qué no, y medir —ejecutado— cuánto detalle carga cada zoom. En la lección 4 seguirás bajando a Component y Code y aprenderás cuándo no seguir. Y en la lección 5 usarás justo estos dos mapas para el ejercicio central del módulo: elegir cuál le das al VP y cuál al dev.

Dos mapas de la misma ciudad: el turístico y el del metro

Piensa en una ciudad que visitas por primera vez. En el aeropuerto tomas dos mapas distintos del mismo lugar. Uno es el mapa turístico: muestra la ciudad como una mancha con sus puntos de interés —el centro histórico, el río, los tres museos famosos, el aeropuerto— y cómo se relacionan a grandes rasgos. Con ese mapa entiendes qué es la ciudad y qué la rodea sin ahogarte en detalle. El otro es el mapa del metro: muestra las líneas, las estaciones, los transbordos —las piezas por las que de verdad te mueves y cómo se conectan—. Con ese mapa ya puedes operar dentro de la ciudad: sabes en qué estación bajarte y dónde cambiar de línea.

Los dos mapas son de la misma ciudad, pero sirven a momentos distintos y a personas distintas. El turista que solo pasa un día y quiere "ver lo principal" usa el turístico. El que va a vivir ahí y moverse a diario necesita el del metro. Y nadie confunde uno con otro: el turístico sería inútil para saber en qué estación cambiar de línea, y el del metro sería abrumador para alguien que solo quiere saber qué tiene la ciudad de interesante.

El Context es el mapa turístico; el Container es el mapa del metro. El Context muestra el sistema como una mancha con lo que lo rodea —quién lo usa, con qué habla— para quien quiere entender qué es. El Container muestra las líneas y estaciones internas —las piezas desplegables y sus conexiones— para quien va a moverse por dentro. Misma "ciudad" (Mercado), dos mapas, dos audiencias. Vamos a dibujar los dos.

Ejemplo trabajado: los dos mapas de Mercado

El Context de Mercado (el mapa del VP)

El Context responde: ¿qué hace Mercado, quién lo usa y de qué sistemas externos depende? Mercado es una sola caja; alrededor, las personas y los sistemas externos. Nada de tecnologías, nada de piezas internas.

C4Context
    title Mercado - System Context (nivel 1, el mapa del VP)
    Person(customer, "Customer", "Busca y compra productos")
    Person(seller, "Seller", "Publica y vende productos")

    System(mercado, "Mercado", "Marketplace en linea: conecta compradores y vendedores")

    System_Ext(payments, "Payment Gateway", "Procesa pagos con tarjeta")
    System_Ext(carrier, "Carrier API", "Genera guias y rastrea envios")

    Rel(customer, mercado, "Busca, compra, rastrea pedidos")
    Rel(seller, mercado, "Publica productos, gestiona inventario")
    Rel(mercado, payments, "Cobra pagos", "HTTPS/API")
    Rel(mercado, carrier, "Solicita envios", "HTTPS/API")

Léelo como lo leería el VP: los clientes buscan y compran; los vendedores publican y venden; Mercado los conecta; para cobrar usa un gateway de pagos externo; para enviar usa un carrier externo. Cinco cajas, cuatro flechas, y la historia completa del negocio en treinta segundos. No aparece PostgreSQL, ni la API, ni el índice de búsqueda —y está bien, porque el VP no vino a eso—. Lo que aparece es exactamente lo que necesita para su pregunta ("¿dónde encaja abrir Mercado a vendedores externos?"): ve que ya hay una relación con vendedores, ve las dependencias externas que quizás toque escalar, y puede decidir sin ahogarse.

El Container de Mercado (el mapa del dev)

Ahora hacemos zoom dentro de la caja de Mercado. El Container responde: ¿de qué piezas desplegables se compone Mercado y cómo se comunican? Las personas y los sistemas externos siguen ahí, como decorado, pero ahora la caja central se abre en sus piezas, cada una con su tecnología.

C4Container
    title Mercado - Containers (nivel 2, el mapa del dev)
    Person(customer, "Customer", "Compra")
    Person(seller, "Seller", "Vende")

    System_Boundary(mercado, "Mercado") {
        Container(web, "Web App", "React", "Storefront en el navegador")
        Container(mobile, "Mobile App", "React Native", "App de compra")
        Container(api, "API", "FastAPI", "Logica de negocio: catalogo, pedidos, checkout")
        ContainerDb(db, "Database", "PostgreSQL", "Productos, pedidos, usuarios")
        Container(search, "Search Index", "Elasticsearch", "Busqueda de catalogo")
    }

    System_Ext(payments, "Payment Gateway", "Stripe")
    System_Ext(carrier, "Carrier API", "Envia")

    Rel(customer, web, "Usa", "HTTPS")
    Rel(customer, mobile, "Usa", "HTTPS")
    Rel(seller, web, "Gestiona productos", "HTTPS")
    Rel(web, api, "Llama", "JSON/HTTPS")
    Rel(mobile, api, "Llama", "JSON/HTTPS")
    Rel(api, db, "Lee y escribe", "SQL")
    Rel(api, search, "Consulta e indexa", "HTTPS")
    Rel(api, payments, "Cobra", "HTTPS/API")
    Rel(api, carrier, "Solicita envios", "HTTPS/API")

Léelo como lo leería el dev nuevo: el cliente usa una web app en React o una app móvil en React Native; las dos llaman a una API en FastAPI que tiene la lógica de negocio; la API lee y escribe en una base de datos PostgreSQL, consulta un índice de búsqueda en Elasticsearch, y habla con los dos sistemas externos —pagos y envíos—. En un minuto, el dev sabe cuáles son las piezas, qué tecnología usa cada una, y cómo fluye una petición desde el navegador hasta la base de datos. Con eso ya puede empezar a leer el código con un mapa en la cabeza, en vez de perderse. Esa es la diferencia entre onboarding en dos días y onboarding en dos semanas.

Cuánto detalle carga cada zoom, medido

Los dos mapas son del mismo sistema, pero cargan cantidades distintas de detalle. Vamos a contarlo, porque "menos detalle" no significa "menos honesto" —significa el detalle correcto para la pregunta de cada audiencia—.

# Cuantos elementos carga cada nivel para las DOS audiencias mas comunes:
# el VP (Context) y el dev tecnico (Container). Menos detalle NO es menos honesto:
# es el detalle correcto para la pregunta que cada quien trae.

# Diagrama Context de Mercado: personas + sistemas externos + Mercado como una caja.
context_elements = [
    ("person", "Customer"),
    ("person", "Seller"),
    ("software_system", "Mercado"),
    ("external_system", "Payment Gateway"),
    ("external_system", "Carrier API"),
]

# Diagrama Container de Mercado: la misma frontera, abierta en piezas desplegables.
container_elements = [
    ("person", "Customer"),
    ("person", "Seller"),
    ("container", "Web App"),
    ("container", "Mobile App"),
    ("container", "API"),
    ("container", "Database"),
    ("container", "Search Index"),
    ("external_system", "Payment Gateway"),
    ("external_system", "Carrier API"),
]

def summarize(name, elements):
    total = len(elements)
    kinds = {}
    for kind, _ in elements:
        kinds[kind] = kinds.get(kind, 0) + 1
    detalle = ", ".join(f"{v} {k}" for k, v in kinds.items())
    print(f"{name:<11} {total:>2} elementos  ({detalle})")

print("El MISMO sistema, dos zooms:")
summarize("Context", context_elements)
summarize("Container", container_elements)
print()
print("El VP mira Context: 5 cajas, cero jerga, entiende que hace Mercado y con quien habla.")
print("El dev mira Container: 9 cajas, ya con tecnologias, sabe donde vive cada cosa.")
print("Ninguno de los dos ve el codigo. Todavia no lo necesitan.")

Qué esperar. Al correrlo:

El MISMO sistema, dos zooms:
Context      5 elementos  (2 person, 1 software_system, 2 external_system)
Container    9 elementos  (2 person, 5 container, 2 external_system)

El VP mira Context: 5 cajas, cero jerga, entiende que hace Mercado y con quien habla.
El dev mira Container: 9 cajas, ya con tecnologias, sabe donde vive cada cosa.
Ninguno de los dos ve el codigo. Todavia no lo necesitan.

Fíjate en lo que cambió y lo que no. Lo que no cambió: las personas (2) y los sistemas externos (2) siguen ahí en ambos —el mundo alrededor de Mercado es el mismo en los dos zooms—. Lo que cambió: la única caja software_system "Mercado" del Context se abrió en 5 containers. Ese es exactamente el movimiento del zoom 1 al zoom 2: abrir la caja del sistema y ver sus piezas, sin tocar el mundo que lo rodea. Y la última línea es clave: ninguno de los dos diagramas muestra código. El VP no lo necesita, el dev nuevo todavía no —primero se orienta con el mapa de la ciudad, y solo baja al código cuando va a tocar una pieza concreta—.

Qué entra y qué no entra en cada nivel

La disciplina del C4 está en ser estricto con qué pertenece a cada nivel. Aquí está la regla para los dos que acabamos de dibujar.

En el Context entra: tu sistema como una sola caja; las personas que lo usan (por rol, no por nombre: "Customer", no "Ana"); los sistemas externos con los que tu sistema habla; y las relaciones entre ellos, etiquetadas en lenguaje de negocio ("compra", "cobra pagos"). No entra: ninguna tecnología, ninguna pieza interna, ninguna base de datos. Si en tu Context aparece la palabra "PostgreSQL" o "API", bajaste de nivel sin querer. La prueba: un Context bien hecho lo entiende alguien que no sabe programar.

En el Container entra: las piezas desplegables de tu sistema (apps, servicios, bases de datos, índices, colas), cada una con su responsabilidad y su tecnología principal; las personas y sistemas externos como decorado alrededor; y las relaciones etiquetadas con cómo se comunican ("JSON/HTTPS", "SQL"). No entra: el interior de cada container —sus clases, sus funciones, sus componentes internos—. Si en tu Container aparece la clase OrderController, bajaste dos niveles sin querer. La prueba: un Container bien hecho le sirve a un dev para saber dónde vive cada cosa, sin decirle todavía cómo está escrita por dentro.

Esta estrictez es lo que mantiene los diagramas legibles. La tentación constante —que la lección 7 llama por su nombre— es "aprovechar" el Container para meter también algún componente interno importante, o "aprovechar" el Context para aclarar que se usa tal base de datos. Cada vez que cedes a esa tentación, mezclas niveles y el diagrama pierde el poder de comunicar a su audiencia. La completitud del sistema no vive en una lámina cargada; vive en el conjunto: Context + Container + (donde haga falta) Component, cada uno limpio.

Las flechas también comunican: etiqueta el cómo, no solo el qué

Es fácil obsesionarse con las cajas y descuidar lo que de verdad hace legible un diagrama: las flechas y sus etiquetas. Una flecha sin etiqueta —o con una etiqueta vaga como "usa" o "se conecta"— desperdicia la mitad del poder comunicativo del diagrama, porque la relación entre dos piezas suele ser tan informativa como las piezas mismas. La regla es que la etiqueta de una flecha responda qué pasa por ahí, en el idioma del nivel en el que estás.

En el Context, las etiquetas van en lenguaje de negocio: "busca y compra", "publica productos", "cobra pagos". El VP lee la flecha entre Mercado y el gateway de pagos y entiende "aquí es donde entra el dinero" sin más. Una etiqueta como "HTTP POST /charge" en el Context sería una fuga de nivel: técnica de más para esa audiencia.

En el Container, las etiquetas ganan un segundo dato —el cómo técnico— sin perder el qué: "llama, JSON/HTTPS", "lee y escribe, SQL", "cobra, HTTPS/API". Fíjate que el C4 permite poner el protocolo o la tecnología en la etiqueta de la relación (por eso escribimos Rel(web, api, "Llama", "JSON/HTTPS") con dos campos): el primero dice qué hace, el segundo cómo viaja. Para el dev, ese "cómo" es oro: le dice si la comunicación es síncrona (una llamada HTTP) o asíncrona (una cola), si cruza la red o es local, qué formato esperar. Un Container con flechas bien etiquetadas le ahorra al dev abrir el código solo para averiguar "¿esto es REST o una cola?".

Hay una asimetría útil en la dirección de la flecha: apunta de quien inicia hacia quien responde —el que hace la petición hacia el que la atiende—. En Mercado, la flecha va de la Web App hacia la API (la web llama a la API, no al revés), y de la API hacia la base de datos (la API consulta la base). Esa dirección comunica el flujo de control de un vistazo: sigues las flechas desde una persona y ves cómo una acción se propaga por el sistema hasta tocar el dato. Un diagrama con flechas en la dirección equivocada, o bidireccionales por pereza ("mejor pongo doble punta por si acaso"), pierde justo esa información. La disciplina: cada flecha, una dirección, una etiqueta que dice qué pasa por ahí en el idioma del nivel.

Errores comunes

Meter tecnología en el Context (de fuga de nivel). Qué pasa: el Context, que debería ser legible para el negocio, termina con etiquetas como "REST API", "microservicios" o "PostgreSQL" porque al arquitecto le parecía "información útil". Por qué pasa: para el arquitecto, la tecnología es la parte interesante, y cuesta resistir el impulso de mencionarla. Cómo detectarlo: enséñale tu Context a alguien no técnico; si pregunta "¿qué es una API?", metiste nivel 2 en el nivel 1. Cómo corregirlo: en el Context, todo se dice en lenguaje de negocio —"Mercado cobra pagos con un proveedor externo", no "la API llama a Stripe vía REST"—; la tecnología es del Container.

Un Container que en realidad es un Component (de zoom de más). Qué pasa: el diagrama de nivel 2 muestra "OrderService", "PaymentService", "TaxCalculator", "NotificationHandler" —pero todos viven dentro de la misma API, no son piezas desplegables separadas—. Eso no es un Container, es un Component disfrazado. Por qué pasa: se confunde "pieza lógica del código" con "pieza desplegable". Cómo detectarlo: pregunta de cada caja "¿esto se despliega y se ejecuta por separado?"; si la respuesta es "no, es una clase dentro de la API", es un componente, no un container. Cómo corregirlo: en el Container solo van cosas que se despliegan por separado (la API entera, la base de datos, el índice); el interior de la API es el nivel 3.

Un Context o Container sin las dependencias externas (de sistema-isla). Qué pasa: el diagrama muestra solo lo que el equipo construyó, y omite el gateway de pagos, el carrier, el proveedor de identidad —los sistemas externos de los que el sistema depende—. Por qué pasa: uno dibuja "lo suyo" y olvida que el sistema no vive solo. Cómo detectarlo: si tu diagrama sugiere que Mercado cobra y envía por arte de magia, sin mostrar de quién depende, mientes por omisión. Cómo corregirlo: el valor de estos niveles está justo en mostrar las fronteras —con quién habla tu sistema—, porque ahí es donde están los riesgos, los costos y los puntos de integración que importan al negocio y al dev.

Ejercicios

Ejercicio 1 — Depura el Context. Un arquitecto te muestra este "Context de Mercado": una caja "Mercado (FastAPI + PostgreSQL)", una caja "Cliente", una caja "Base de datos PostgreSQL", una caja "Stripe", y una flecha de Mercado a "AWS S3 (imágenes de productos)". Tres cosas están mal para un Context. Encuéntralas y corrígelas.

Ver solución

Error 1: la tecnología en la caja del sistema. "Mercado (FastAPI + PostgreSQL)" mete nivel 2 en el nivel 1. En el Context la caja debe decir solo "Mercado — Marketplace en línea", sin tecnologías. El VP no necesita saber que corre en FastAPI.

Error 2: la base de datos como caja del Context. "Base de datos PostgreSQL" es una pieza interna del sistema —un container—, no un actor del mundo que lo rodea. No pertenece al Context; aparecerá cuando hagamos zoom al Container. En el Context, la base de datos está dentro de la caja de Mercado, invisible.

Error 3 (más sutil): mezcla de niveles de detalle en los externos. Stripe (gateway de pagos) y AWS S3 (almacenamiento de imágenes) son ambos sistemas externos válidos en el Context, así que tenerlos no está mal por sí solo; lo que está mal es la inconsistencia con el resto —si mostramos S3 pero omitimos el carrier de envíos, damos una foto sesgada—. La corrección de fondo: en el Context, muestra las dependencias externas significativas para el negocio de forma pareja (pagos, envíos, y sí, almacenamiento si es relevante), todas al mismo nivel de abstracción y en lenguaje de negocio ("guarda imágenes de productos", no "AWS S3 bucket").

El Context corregido: Mercado (una caja, sin tech) ← Customer y Seller (personas) → y Mercado habla con Payment Gateway, Carrier API y almacenamiento de imágenes (externos, en lenguaje de negocio). La base de datos desaparece de este nivel.

Ejercicio 2 — Del Context al Container. Tienes el Context de un sistema de reservas de restaurantes: una caja "ReservaYa" usada por "Comensal" y "Restaurante", que habla con un "Proveedor de SMS" externo. Te piden el Container. El sistema por dentro tiene: una app web, una app móvil, una API, una base de datos, y un worker que envía recordatorios por SMS. Dibuja (en texto o mermaid) cómo se vería el Container, y di qué del Context se conserva y qué se abre.

Ver solución

Se conservan los actores del mundo: Comensal y Restaurante (personas) y el Proveedor de SMS (externo) siguen igual, como decorado. Se abre la caja "ReservaYa" en sus piezas desplegables.

C4Container
    title ReservaYa - Containers
    Person(diner, "Comensal", "Reserva mesa")
    Person(resto, "Restaurante", "Gestiona disponibilidad")
    System_Boundary(reservaya, "ReservaYa") {
        Container(web, "Web App", "React", "Reservas desde el navegador")
        Container(mobile, "Mobile App", "Flutter", "Reservas desde el movil")
        Container(api, "API", "Node.js", "Logica de reservas")
        ContainerDb(db, "Database", "PostgreSQL", "Reservas, mesas, usuarios")
        Container(worker, "Reminder Worker", "Python", "Envia recordatorios")
    }
    System_Ext(sms, "Proveedor de SMS", "Envia mensajes")
    Rel(diner, web, "Usa", "HTTPS")
    Rel(diner, mobile, "Usa", "HTTPS")
    Rel(resto, web, "Gestiona", "HTTPS")
    Rel(web, api, "Llama", "JSON/HTTPS")
    Rel(mobile, api, "Llama", "JSON/HTTPS")
    Rel(api, db, "Lee y escribe", "SQL")
    Rel(worker, db, "Lee reservas proximas", "SQL")
    Rel(worker, sms, "Envia recordatorios", "API")

Lo interesante del ejercicio es el worker: es un container aunque no tenga interfaz de usuario, porque se despliega y se ejecuta por separado. Y fíjate que el "Proveedor de SMS" que en el Context hablaba con "ReservaYa" (la caja entera), en el Container se ve que en realidad habla con una pieza concreta —el worker—. Ese es el valor de bajar de nivel: las relaciones vagas del Context se vuelven precisas en el Container.

Ejercicio 3 — ¿Qué mapa pido? Para cada situación en Mercado, di si necesitas mostrar el Context o el Container, y por qué: (a) el equipo de ventas quiere una lámina para el pitch a un inversionista; (b) un dev de otro equipo va a construir un servicio que consuma pedidos de Mercado y pregunta "¿con qué hablo y cómo?"; (c) la líder de producto quiere entender, antes de aprobar un proyecto, qué partes del sistema tocaría añadir "listas de deseos".

Ver solución

(a) El pitch al inversionista → Context. El inversionista trae la pregunta del mundo: ¿qué es esto, quién lo usa, de qué depende? Cinco cajas sin jerga cuentan la historia del negocio y caben en una diapositiva. Un Container lo perdería en tecnologías que no evalúa.

(b) El dev que va a integrar → Container. Necesita saber con qué pieza habla y cómo —¿hay una API?, ¿qué protocolo?—. El Container le muestra la API como container, con su tecnología y sus conexiones, que es justo lo que necesita para integrar. El Context sería muy poco (no vería la API); el Component sería de más (no le importa cómo está partida la API por dentro, solo su contrato externo).

(c) La líder de producto que evalúa "listas de deseos" → depende, pero probablemente empieza en Context y quizás un vistazo al Container. Si su pregunta es puramente de negocio ("¿esto encaja en lo que hacemos?"), el Context basta. Pero como pregunta específicamente "qué partes tocaría", ya está pidiendo algo del mapa de la ciudad: querrá ver que la funcionalidad probablemente vive cerca del catálogo y los usuarios, lo cual se aprecia mejor en un Container simplificado. La respuesta madura: usa el Context para el marco ("aquí está el sistema") y señala sobre un Container acotado qué piezas se tocarían, sin ahogarla en las cinco. Muestra el nivel mínimo que responde su pregunta y ni uno más.

Resumen y siguiente paso

En esta lección dibujaste los dos mapas que usarás el 80% del tiempo: el Context de Mercado (el mapa turístico: cinco cajas, cero jerga, para el VP y el negocio) y el Container de Mercado (el mapa del metro: nueve cajas con tecnologías, para el dev y ops). Viste, ejecutado, que bajar del zoom 1 al zoom 2 es literalmente abrir la caja del sistema en sus piezas sin tocar el mundo que lo rodea —las personas y los externos se conservan, la única caja de Mercado se vuelve cinco—. Y fijaste la disciplina de qué entra en cada nivel: cero tecnología en el Context, cero clases en el Container, y siempre las dependencias externas visibles porque ahí están las fronteras que importan.

Antes de avanzar deberías poder: dibujar un Context legible para el negocio (sistema como una caja, personas, externos, lenguaje de negocio) y un Container legible para devs (piezas desplegables con tecnología, decorado externo); explicar qué se conserva y qué se abre al pasar de uno al otro; y distinguir un container de verdad (se despliega por separado) de un componente disfrazado (una clase dentro de una pieza).

Lo que sigue es seguir bajando —y aprender cuándo no hacerlo—. En la lección 4 vas a dibujar el Component del container de checkout (el mapa del barrio, para el dev que trabaja dentro de esa pieza) y a ver por qué el Code (el mapa de la calle) casi nunca se dibuja a mano. Vas a medir, ejecutado, cuántos diagramas de verdad vale la pena mantener en Mercado —spoiler: tres, no treinta— y a entender la regla del zoom: bajas un nivel solo donde el detalle ayuda a alguien real a trabajar.

Recursos