strategic · temario
2.3tema 3 de 5

Context Mapping

Los patrones de context mapping describen el tipo de relación y nivel de acoplamiento entre bounded contexts.

Un context map es el inventario de relaciones entre bounded contexts: qué contexto depende de cuál, cómo se comunican y con qué nivel de autonomía. Sin un mapa explícito, las dependencias crecen de forma orgánica y producen acoplamiento oculto que es difícil de detectar y costoso de cambiar.

DDD cataloga nueve patrones de relación. Cada patrón describe una realidad organizacional diferente: equipos que colaboran de forma muy estrecha (Partnership), contextos que comparten un núcleo común (Shared Kernel), relaciones asimétricas donde uno dicta y el otro conforma (Customer-Supplier / Conformist), y estrategias de protección o publicación (ACL, OHS, Published Language). También reconoce dos situaciones de no-relación o deuda (Separate Ways, Big Ball of Mud).

La distinción upstream / downstream es central: el upstream provee el modelo y el downstream lo consume. Si el upstream cambia, el downstream puede verse afectado. El patrón elegido determina cuánta autonomía tiene cada lado y qué mecanismo protege al downstream de cambios no coordinados.

Los patrones no son mutuamente excluyentes: una misma relación puede combinar Customer-Supplier con Published Language (el upstream publica un contrato versionado) y el downstream puede agregar además una ACL para aislar su modelo interno del contrato externo.

Elegir el patrón correcto es una decisión técnica y organizacional. Partnership requiere que ambos equipos puedan coordinarse frecuentemente. Conformist es una rendición pragmática cuando el equipo downstream no tiene poder de negociación. Big Ball of Mud es una descripción honesta de legacy sin estructura, útil para planificar la salida progresiva de ese estado.

structure.txt
# Context Map — ASCII diagram (generic)

   [<ContextA>] ──── Partnership ────> [<ContextB>]
        |
        | Customer-Supplier (A=upstream, C=downstream)
        v
   [<ContextC>] ── Conformist ──> (adopts A's model as-is)

   [<ContextD>] ──OHS/PL──> published API ──ACL──> [<ContextE>]
        D exposes a stable Open Host Service with a Published Language;
        E protects its own model with an Anti-Corruption Layer.

# ACL adapter example (Python)
# contexts/<context_e>/infrastructure/acl/<context_d>_acl.py

from src.project.contexts.context_e.domain.model.<entity> import <Entity>
from src.project.contexts.context_e.domain.model.<value_obj> import <ValueObject>

class <ContextD>Acl:
    """Translates <ContextD> external model into <ContextE> domain model."""

    def to_<entity>(self, external_dto: dict) -> <Entity>:
        return <Entity>(
            id=<EntityId>(external_dto['id']),
            <field>=<ValueObject>(external_dto['<external_field>']),
        )

# OHS adapter example
# contexts/<context_d>/infrastructure/api/controllers.py
# Exposes a stable, versioned API — the Published Language

# router = APIRouter(prefix='/v1/<context_d>')
# @router.get('/<resource>/{id}')
# def get_<resource>(id: str) -> <PublishedLanguageSchema>: ...

Debugging lab

Detecta y corrige el error o la violación de diseño.

0/5 tests passing0%
  1. 2.3.5.1

    # ContextA and ContextB share business-logic code in shared_kernel: # shared_kernel/domain/order_pricing.py def calculate_discount(order: Order, customer: Customer) -> Money: # complex pricing logic here ...

  2. 2.3.5.2

    # ContextB calls ContextA's internal repository directly (no ACL): from src.project.contexts.context_a.infrastructure.persistence.sql_aggregate_repository import SqlAggregateRepository class ContextBService: def __init__(self, repo: SqlAggregateRepository): ...

  3. 2.3.5.3

    # Conformist pattern chosen but an ACL was actually built: # contexts/context_c/infrastructure/acl/context_a_acl.py # ... translates every field from ContextA into ContextC's model

  4. 2.3.5.4

    # OHS has no versioning — breaking changes are deployed directly: # contexts/context_d/infrastructure/api/controllers.py # @router.get('/<resource>/{id}') # no version prefix def get_resource(id: str) -> dict: return {'id': id, 'field': 'value'} # field renamed next sprint

  5. 2.3.5.5

    # Big Ball of Mud: contexts are importing each other in cycles: # context_a imports context_b.domain # context_b imports context_c.domain # context_c imports context_a.domain