Be Software Architecture Wise
Be Wise Apps

Read in English · Lire en français · Auf Deutsch lesen

Sección 3.2 de 3.8 · Intermedio · 24 min de lectura

Acoplamiento y cohesión

Videolección

5 min · narrado en inglés, con subtítulos
Solo escuchar (audio)
Leer la transcripción

El acoplamiento y la cohesión son las dos medidas más antiguas de una buena estructura. En esta lección aprenderás sus niveles clásicos, cómo contar las dependencias, los tipos adicionales de acoplamiento entre servicios y cómo debilitarlo.

Piensa en dos vecinos que comparten un jardín. Si solo se prestan alguna herramienta de vez en cuando, cualquiera de los dos puede rediseñar su propio jardín sin problemas. Están débilmente acoplados. Si comparten un cobertizo, una manguera y una llave debajo de una maceta, cada cambio exige una conversación. Están fuertemente acoplados. Ahora piensa en un cajón de la cocina. Si solo contiene cubiertos, sabes dónde buscar. Es cohesivo. Si contiene cubiertos, pilas, cordel y recibos viejos, no lo es. Un buen software tiene partes cohesivas que están débilmente acopladas entre sí.

El acoplamiento es el grado en que un módulo depende de otro, de modo que un cambio en uno puede obligar a cambiar el otro. La cohesión es el grado en que los elementos de un módulo forman un todo, porque sirven a un único propósito. Pero el acoplamiento no es un defecto. Un sistema cuyas partes nunca dependieran unas de otras no haría nada útil. El objetivo es elegirlo de forma deliberada: pocas dependencias, a través de interfaces estrechas y estables, y tan débil como permita la tarea.

El diseño estructurado, de la década de 1970, ordenó las formas en que pueden conectarse los módulos, de la más fuerte, a la izquierda, a la más débil, a la derecha. El acoplamiento de contenido consiste en meterse en las partes internas de otro módulo, como un servicio que escribe en las tablas de la base de datos de otro servicio. El acoplamiento común consiste en compartir datos globales, o un mismo esquema de base de datos. El acoplamiento externo consiste en compartir un formato impuesto, como la estructura de archivo de un socio. El acoplamiento de control consiste en pasar un indicador que le dice al otro módulo qué hacer. El acoplamiento de marca pasa un registro entero cuando solo se necesita una parte. El acoplamiento de datos, el más débil, pasa solo datos simples que se necesitan todos, a través de una interfaz estrecha y documentada. La cohesión pregunta por qué están juntas las cosas de un módulo. En el extremo débil, la coincidental: un módulo utils que contiene el formateo de fechas y un tipo de IVA. En el extremo fuerte, la funcional: un módulo Tax que calcula el IVA, y solo eso.

Los niveles describen el tipo de dependencia. Los arquitectos también las cuentan. El acoplamiento aferente es el número de módulos que dependen de un componente: sus flechas entrantes. El acoplamiento eferente es el número de módulos de los que depende: sus flechas salientes. Los términos vienen de la anatomía, donde los nervios aferentes llevan las señales hacia dentro y los eferentes, hacia fuera. Un componente con un acoplamiento aferente alto, como una biblioteca de dinero que usan cuarenta módulos, es responsable. Cualquier cambio puede romper muchos otros, así que debe cambiar con cuidado y rara vez. Uno con un acoplamiento eferente alto, como un flujo de compra que llama a otros cinco módulos, está expuesto. El problema llega cuando un componente es ambas cosas.

Entre servicios aparecen tres tipos más, y ninguno se ve en la firma de una función. El acoplamiento temporal significa que una parte solo funciona si otra está disponible en ese mismo momento. Si el proceso de pago llama directamente a pagos y pagos está caído, el proceso de pago también lo está. El acoplamiento de despliegue significa que dos partes deben lanzarse juntas. Si cada lanzamiento de Pedidos necesita un lanzamiento de Facturación, en la práctica son una sola unidad, diga lo que diga el diagrama. El acoplamiento semántico consiste en estar de acuerdo en un significado que ninguna interfaz expresa, como que el estado cuatro significa enviado, o que los precios están en peniques. Estos acuerdos se rompen en silencio.

Veamos una refactorización práctica. Antes, el módulo Pedidos de una tienda en línea llama directamente a Facturación en cada pedido y le pasa un indicador de modo. Eso es acoplamiento de control, y también acoplamiento temporal: Pedidos espera a Facturación. Facturación también lee directamente la tabla de pedidos, y descifra sus códigos de estado y su columna del IVA. Eso es acoplamiento común, y también acoplamiento semántico. Después, Pedidos publica un evento Order Placed a través de un intermediario de mensajes, con exactamente los datos que necesita Facturación, incluidos los totales que Pedidos ya ha calculado. Cada servicio es propietario de sus propios datos. Lo que queda es acoplamiento de datos, a través de un contrato pequeño y publicado. Y Pedidos ya no espera a Facturación.

Midamos lo que ha cambiado. Antes, una tabla compartida, un indicador de modo y códigos de estado ocultos. Después, un solo evento versionado con cinco campos. Antes, un pedido fallaba si Facturación estaba caída. Ahora el evento espera en el broker hasta que Facturación se recupera. Antes, cambiar la tabla de pedidos exigía un lanzamiento conjunto. Ahora Pedidos puede cambiarla libremente. Pero hay costos nuevos: un broker que operar, facturas unos segundos más tarde y un evento que ahora es un contrato público. El acoplamiento no se eliminó, se eligió. Ahora es explícito, estrecho y versionado.

La sección completa tiene tablas de todos los niveles, un ejemplo práctico en el que se cuentan dependencias y las tácticas para debilitar el acoplamiento, cada una con su precio. A continuación, en la sección tres punto tres, veremos la ocultación de información. Nos vemos allí.

Lo que necesitas saber

Dos medidas de la estructura

El acoplamiento es el grado en que un módulo depende de otro, de modo que un cambio en uno puede obligar a cambiar el otro. La cohesión es el grado en que los elementos de un módulo forman un todo porque sirven a un único propósito. Ambas ideas las introdujo Larry Constantine a finales de la década de 1960 como parte del movimiento del diseño estructurado, y se publicaron en el artículo Structured Design de Wayne Stevens, Glenford Myers y Constantine en el IBM Systems Journal de 1974, y después con más extensión en el libro de Edward Yourdon y Constantine Structured Design (1979). La Sección 0.3 las presentó de manera informal; esta sección las precisa.

Las dos están relacionadas. Si las cosas que cambian juntas viven en el mismo módulo (cohesión alta), menos cambios cruzan los límites entre módulos, y los módulos necesitan saber menos unos de otros (acoplamiento bajo). Yourdon y Constantine llegaron a decir que, en la práctica, la cohesión era la más importante de las dos. Las tácticas de modificabilidad de la Sección 2.6 (aumentar la cohesión, reducir el acoplamiento) se basan exactamente en esta pareja.

Una aclaración desde el principio: el acoplamiento no es un defecto. Un sistema cuyos módulos nunca dependieran unos de otros no haría nada útil. Martin Fowler, que escribió sobre el tema en 2001, advirtió que intentar eliminar por completo el acoplamiento solo empuja todo a un módulo enorme en el que el acoplamiento está oculto, no desaparecido. El objetivo es elegir el acoplamiento de forma deliberada: pocas dependencias, a través de interfaces estrechas y estables, que apunten en direcciones sensatas y tan débiles como permita la tarea.

Las escalas clásicas

La escala clásica de acoplamiento, del más fuerte (contenido) al más débil (datos), y la de cohesión, de la más débil (coincidental) a la más fuerte (funcional)Acoplamiento entre módulos: apunta al extremo derechoContenidoComúnExternoControlMarcaDatosmás fuerte, peormás débil, mejorCohesión dentro de un módulo: apunta al extremo derechoCoinci-dentalLógicaTemporalProcedim.Comunica-cionalSecuencialFuncionalmás débil, peormás fuerte, mejor
Las escalas del diseño estructurado: el acoplamiento, del de contenido (el más fuerte) al de datos (el más débil), y la cohesión, de la coincidental (la más débil) a la funcional (la más fuerte).

Los niveles de acoplamiento

El diseño estructurado ordenó las formas en que pueden conectarse dos módulos, de la más perjudicial a la más inofensiva. La forma de seis niveles que aparece a continuación suele atribuirse a los libros de Glenford Myers sobre diseño compuesto de mediados de la década de 1970, y más tarde la popularizó Meilir Page-Jones; los nombres y los límites varían ligeramente entre fuentes, pero esta es la forma que recogen la mayoría de los libros de texto. Se definieron para procedimientos dentro de un programa, pero cada uno tiene un equivalente directo entre componentes y servicios, que es donde los encuentran hoy los arquitectos.

Niveles de acoplamiento, del más fuerte al más débil

NivelQué significaEn el códigoEntre servicios
ContenidoUn módulo se mete en las partes internas de otro: cambia sus datos privados o salta dentro de su código.Asignar cart._items desde fuera; aplicar un monkey-patch a la función de otro módulo.Leer o escribir directamente en las tablas de la base de datos de otro servicio.
ComúnVarios módulos leen y escriben los mismos datos globales.Un diccionario global config que cualquier función puede cambiar.Varios servicios que comparten un esquema de base de datos o un espacio de claves de caché compartido.
ExternoLos módulos comparten un formato, un protocolo o un dispositivo impuestos desde fuera.Dos módulos analizan el mismo formato de archivo de un proveedor.Varios servicios que dependen de la estructura de archivo de un socio o de una versión concreta de un protocolo de mensajes.
ControlUn módulo pasa un indicador que le dice al otro qué hacer, de modo que quien llama conoce la lógica interna de quien es llamado.process(order, mode="refund")Una API con un campo action que selecciona comportamientos completamente distintos.
MarcaUn módulo recibe una estructura de datos entera, pero solo usa una parte.Pasar el objeto Customer completo a una función que solo necesita la dirección de correo electrónico.Enviar el registro completo del cliente en cada evento cuando los consumidores necesitan dos campos.
DatosLos módulos se comunican solo mediante parámetros de datos simples, todos ellos necesarios.send_receipt(email, order_id, total)Una API o un evento estrecho y documentado, con solo los campos que necesitan los consumidores.

Dos advertencias. Primero, la escala es una guía del riesgo, no una ley. El acoplamiento de marca suele ser un intercambio sensato: pasar un objeto entero es más sencillo que desempaquetar seis campos, y el único costo es que el receptor puede romperse ahora cuando cambian campos que ni siquiera usa. Segundo, la escala no dice nada sobre cuántas dependencias hay ni sobre la frecuencia con que cambia la otra parte. Una sola llamada con acoplamiento de control a un módulo que nunca cambia puede ser inofensiva; el acoplamiento de datos con un módulo que cambia cada semana puede hacer daño. Más adelante en este capítulo, la connascence ofrece un vocabulario más fino, y Balancing Coupling in Software Design (2024), de Vlad Khononov, sostiene que la fuerza del acoplamiento debe juzgarse siempre junto con la distancia entre los módulos y su volatilidad.

# Control coupling: the caller chooses the callee's internal branch
def handle_payment(order, mode):
    if mode == "charge":
        ...  # call the provider, store the transaction
    elif mode == "refund":
        ...  # look up the transaction, reverse it
    elif mode == "dry_run":
        ...  # validate only

handle_payment(order, "refund")

# Data coupling: separate operations, each taking only what it needs
def charge(order_id: str, amount_pence: int) -> str: ...
def refund(transaction_id: str, amount_pence: int) -> None: ...

refund("tx_81f2", 1999)
Acoplamiento de control (arriba) frente a acoplamiento de datos (abajo): el indicador obliga a quien llama a saber cómo está organizado por dentro quien es llamado.

Los niveles de cohesión

La cohesión pregunta por qué están juntos los elementos de un módulo. Yourdon y Constantine describieron siete tipos de asociación, también de peor a mejor. Los niveles inferiores agrupan cosas por razones que no tienen que ver con el problema (se escribieron por casualidad al mismo tiempo, o se ejecutan en el mismo momento); los niveles superiores agrupan cosas porque trabajan sobre los mismos datos o hacia un mismo propósito único. Los resúmenes posteriores de los libros de texto coinciden en el orden general: la cohesión coincidental y la lógica son pobres, la comunicacional y la secuencial son buenas y la funcional es la mejor, aunque los saltos entre niveles no están espaciados de manera uniforme.

Niveles de cohesión, del más débil al más fuerte

NivelLos elementos están agrupados porque...Ejemplo
Coincidentalno hay ninguna relación significativa entre ellos.Un módulo utils que contiene el formateo de fechas, un decorador de reintentos y un tipo de IVA.
Lógicahacen el mismo tipo de cosa, seleccionada mediante un indicador.Un módulo exports con una función que escribe CSV, PDF o XML según un parámetro.
Temporalse ejecutan al mismo tiempo.Una rutina startup() que abre la base de datos, precarga las cachés y envía un ping de monitorización.
Procedim.se suceden en una secuencia de control, aunque sobre datos distintos.Una función que valida un formulario, después registra la dirección IP del usuario y después actualiza un menú.
Comunicacionaloperan sobre los mismos datos.Un módulo que calcula varios informes a partir del mismo historial de pedidos.
Secuencialla salida de uno es la entrada del siguiente.Analizar un extracto bancario, después clasificar sus líneas y después totalizar cada categoría.
Funcionaljuntos realizan una tarea bien definida y nada más.Un módulo Tax que calcula el IVA de un carrito, y solo eso.

A escala de arquitectura, la cohesión es la cuestión de si un componente o un servicio tiene una sola razón para existir. Un servicio llamado CustomerService que gestiona el inicio de sesión, los puntos de fidelidad y las preferencias de marketing tiene, como mucho, cohesión lógica: tres equipos lo cambiarán por tres motivos sin relación entre sí. Una buena heurística es describir el propósito de un módulo en una sola frase sin usar la palabra y; la Sección 3.5 desarrolla esta idea como el principio de responsabilidad única, definido en función de las personas que solicitan los cambios.

Acoplamiento aferente y eferente

Los niveles anteriores describen el tipo de una dependencia. Los arquitectos también necesitan contarlas. El acoplamiento aferente (Ca) es el número de módulos externos a un componente que dependen de él: sus flechas entrantes. El acoplamiento eferente (Ce) es el número de módulos externos de los que depende: sus flechas salientes. Los términos vienen de la anatomía, donde los nervios aferentes llevan las señales hacia dentro y los eferentes, hacia fuera. Robert C. Martin los definió para paquetes en 1994 contando clases; las herramientas actuales cuentan clases, módulos o paquetes, así que comprueba siempre qué cuenta una herramienta antes de comparar cifras.

Las dos cifras cuentan historias distintas. Un componente con un Ca alto, como una biblioteca compartida money que usan cuarenta módulos, es responsable: cualquier cambio en él puede romper muchos otros, así que debe cambiar con cuidado y rara vez. Un componente con un Ce alto, como un flujo de compra que llama a precios, existencias, pagos, entregas y notificaciones, está expuesto: puede necesitar cambios cada vez que cambie cualquiera de ellos. Ninguna de las dos situaciones es mala en sí misma; el problema surge cuando de un componente dependen muchos otros y, a la vez, él mismo depende de muchas cosas volátiles. La Sección 3.4 convierte Ca y Ce en la métrica de inestabilidad de Martin y en las reglas que se basan en ella.

Ejemplo práctico

Ejemplo práctico: contar Ca y Ce en una app de reparto de comida

Siete módulos (Caja, Carta, Precios, Pagos, Notificaciones, Seguimiento de repartidores y Administración) tienen estas dependencias ("A depende de B" se escribe "A a B"): Caja a Carta, Caja a Precios, Caja a Pagos, Caja a Notificaciones; Seguimiento de repartidores a Notificaciones; Precios a Carta; Administración a Carta, Administración a Precios.

CartaCa = 3 (Caja, Precios, Administración); Ce = 0
PreciosCa = 2 (Caja, Administración); Ce = 1 (Carta)
NotificacionesCa = 2 (Caja, Seguimiento de repartidores); Ce = 0
PagoCa = 0; Ce = 4 (Carta, Precios, Pagos, Notificaciones)
LecturaCarta es el módulo del que más se depende, así que su interfaz necesita el máximo cuidado y los cambios más lentos. Caja es el que más depende de otros, así que debería ser el más fácil de cambiar y el que absorba los cambios de los demás.

Contar lleva unos minutos con una herramienta de dependencias y convierte una impresión vaga ("Carta es central") en una cifra que puedes seguir a lo largo del tiempo.

El acoplamiento en los sistemas distribuidos

Cuando los módulos se convierten en servicios que se comunican a través de una red, los niveles clásicos siguen siendo válidos, pero cobran importancia otros tres tipos de acoplamiento. Ninguno de ellos es visible en la firma de una función, por eso los equipos que dividen un sistema en servicios suelen sorprenderse de lo acoplados que siguen estando los servicios.

El acoplamiento temporal significa que una parte solo funciona si otra está disponible y responde en ese mismo momento. Una llamada HTTP síncrona de Caja a Pagos acopla su disponibilidad: si Pagos cae, también cae el proceso de pago, y la disponibilidad combinada es el producto de las dos (Sección 2.5). Los mensajes asíncronos a través de una cola eliminan este acoplamiento, a cambio de resultados eventuales en lugar de inmediatos. (En el diseño a nivel de código, el mismo término significa a veces que las llamadas deben hacerse en un orden fijo, como open() antes de read(); normalmente el contexto deja claro el significado).

El acoplamiento de despliegue significa que dos partes deben lanzarse juntas, o en un orden fijo, porque un cambio en una rompe la otra. Si cada lanzamiento de Pedidos necesita un lanzamiento simultáneo de Facturación, en la práctica los dos son una sola unidad desplegable, diga lo que diga el diagrama. Los cambios compatibles con versiones anteriores, las API versionadas y el patrón expandir y contraer son los remedios habituales.

El acoplamiento semántico significa que dos partes deben estar de acuerdo en un significado que ninguna interfaz expresa: que un pedido con status = 4 está enviado, que los precios están en peniques e incluyen el IVA, que un "cliente activo" ha hecho un pedido en los últimos 90 días. Estos acuerdos se rompen en silencio cuando una de las partes cambia su interpretación. El remedio es hacer explícito el significado en el contrato (estados con nombre, unidades en los nombres de los campos, esquemas publicados) o mantener la regla en un único lugar al que los demás consultan.

Tácticas para reducir el acoplamiento

Interfaces estrechas. Expón el menor número de operaciones y campos que necesiten los clientes. Cada campo adicional en una respuesta de una API es algo de lo que un cliente puede llegar a depender (Sección 3.3, sobre la ley de Hyrum). Intermediarios. Pon algo entre las dos partes para que ninguna conozca los detalles de la otra: un adaptador o una capa anticorrupción que traduzca el modelo de un proveedor, un API gateway o un intermediario de mensajes (message broker) que desacople al emisor del receptor en el tiempo y en la identidad. Eventos en lugar de comandos. Un comando (Facturación, crea una factura) obliga al emisor a conocer al receptor y su tarea; un evento (se ha hecho un pedido) permite al emisor declarar un hecho y a cualquier número de receptores reaccionar, así que se pueden añadir consumidores nuevos sin cambiar al productor. Propiedad de los datos. Cada dato tiene un único propietario que lo escribe; los demás módulos se lo piden al propietario a través de su interfaz o mantienen su propia copia alimentada por eventos, sin leer nunca las tablas del propietario (Sección 7.1).

Toda táctica tiene un precio. Los intermediarios añaden un salto y otra cosa más que operar. Los eventos renuncian a la consistencia inmediata y hacen más difícil ver en un solo lugar el flujo de un proceso de negocio. Las copias de datos pueden estar desactualizadas. La propiedad de los datos obliga a construir y versionar API que una tabla compartida te daba gratis. El compromiso merece la pena donde el cambio es frecuente o los equipos están separados, y a menudo no la merece entre dos clases que mantiene una sola persona.

Ejemplo práctico: refactorizar una pareja de pedidos y facturación fuertemente acoplada

El módulo Pedidos de una tienda en línea llama directamente a Facturación cada vez que se hace un pedido. Después, Facturación lee la tabla de pedidos para obtener los detalles, interpreta los códigos numéricos de la columna de estado y vuelve a calcular el IVA a partir de los precios de las líneas. El código siguiente es una versión simplificada; muestra tres de los problemas a la vez.

# orders.py
def place_order(basket, customer_id):
    order_id = db.insert("orders", customer_id=customer_id, status=1,
                         lines=basket.lines)
    billing.process(order_id, mode="invoice")   # Orders waits for Billing
    return order_id

# billing.py
def process(order_id, mode):
    row = db.query_one("SELECT * FROM orders WHERE id = %s", order_id)
    if row["status"] != 1:              # 1 means 'placed', by convention
        return
    net = sum(l["price"] * l["qty"] for l in row["lines"])
    vat = round(net * 0.2)               # VAT rule duplicated here
    if mode == "invoice":
        db.insert("invoices", order_id=order_id, net=net, vat=vat)
    elif mode == "credit_note":
        db.insert("credit_notes", order_id=order_id, net=-net, vat=-vat)
Antes: acoplamiento de control (el indicador de modo), acoplamiento común (ambos módulos usan la tabla de pedidos) y acoplamiento semántico (los dos conocen los códigos de estado y las reglas del IVA).

Cualquier cambio en la estructura de la tabla de pedidos, en los códigos de estado o en las reglas del IVA tiene que hacerse ahora en los dos módulos a la vez. Pedidos no puede aceptar un pedido si Facturación va lenta o está fallando. Y los dos comportamientos de Facturación se seleccionan con un indicador que Pedidos tiene que conocer. La refactorización se hace en cuatro pasos: Pedidos deja de llamar a Facturación y, en su lugar, publica un evento OrderPlaced con exactamente los datos que necesita Facturación, incluidos los totales que Pedidos ya ha calculado; Facturación se suscribe y registra las facturas en su propio almacén; los dos comportamientos se convierten en dos operaciones; y la tabla de pedidos pasa a ser privada de Pedidos.

# orders.py
def place_order(basket, customer_id):
    order = Order.create(customer_id, basket)      # computes net and VAT once
    repository.save(order)
    events.publish("OrderPlaced", {
        "schema_version": 1, "order_id": order.id,
        "customer_id": customer_id,
        "net_pence": order.net_pence, "vat_pence": order.vat_pence})
    return order.id

# billing.py
@events.subscribe("OrderPlaced")
def on_order_placed(event):
    create_invoice(event["order_id"], event["net_pence"], event["vat_pence"])

def create_invoice(order_id, net_pence, vat_pence): ...
def create_credit_note(order_id, net_pence, vat_pence): ...
Después: Pedidos declara un hecho mediante un evento versionado; Facturación es propietaria de sus propios datos y ya no conoce las tablas, los códigos ni las reglas fiscales de Pedidos.

La pareja de pedidos y facturación, antes y después

Antes: Facturación lee la base de datos de Pedidos y recibe un indicador de modo. Después: Pedidos publica un evento OrderPlaced y Facturación guarda sus propios datosAntesPedidosFacturaciónindic. modotabla pedidos(de Pedidos)lee filas,códigos estado,columna IVAAcoplamiento común por la tabla,de control por el indicador,temporal: Pedidos esperaa Facturación en cada pedidoDespuésPedidosFacturaciónBroker demensajesevento OrderPlaced(ids, totales, versión)BD pedidossolo PedidosBD facturassolo Factur.Acoplamiento de datos por un contratopublicado; cada servicio, sus datos;Pedidos ya no espera a Facturación
La refactorización sustituye una llamada directa con un indicador y una tabla compartida por un evento publicado y almacenes de datos separados.

Ejemplo práctico

Qué cambió, medido

Comparación de las dos versiones con el vocabulario de acoplamiento de esta sección.

Tipo de acoplamientoAntes: casi de contenido y común (tabla compartida), de control (indicador de modo), semántico (estado 1, regla del IVA). Después: acoplamiento de datos mediante un evento versionado de cinco campos.
Acoplamiento eferente de PedidosAntes: Ce incluye Facturación. Después: Pedidos depende solo del contrato del evento y del broker; no sabe que Facturación existe.
Acoplamiento temporalAntes: un pedido falla si Facturación está caída. Después: el evento espera en el broker hasta que Facturación se recupera.
Acoplamiento de despliegueAntes: un cambio en la tabla de pedidos necesita un lanzamiento conjunto. Después: Pedidos puede cambiar su tabla libremente; solo los cambios en el evento requieren cuidado, y el versionado se encarga de ellos.
Costos nuevosUn broker que operar; las facturas aparecen unos segundos después en lugar de al instante; el esquema del evento es ahora un contrato público que hay que versionar; el proceso es más difícil de rastrear sin una buena observabilidad.

El evento lleva los totales que calculó Pedidos, así que el IVA se calcula en un solo lugar. Facturación ya no necesita conocer las reglas fiscales ni la estructura de las líneas de pedido.

La refactorización eliminó una elección, no todo el acoplamiento: Facturación sigue dependiendo del significado de OrderPlaced. Esa dependencia es ahora explícita, estrecha y versionada, que es lo que significa en la práctica el acoplamiento débil.

Cómo usan los profesionales estas ideas

Los diseñadores con experiencia rara vez citan los nombres de los niveles en las revisiones, pero los aplican constantemente: cuestionan cada parámetro que actúa como indicador, buscan tablas compartidas y estado global, preguntan cuál es el único propósito de cada módulo y observan qué módulos cambian juntos en el control de versiones. En los límites entre equipos y servicios son estrictos, porque allí el acoplamiento es caro de deshacer; dentro de un módulo pequeño del que es propietaria una sola persona son flexibles, porque allí es barato.

También hacen visible el acoplamiento. Un grafo de dependencias generado a partir del código, un recuento de Ca y Ce por componente y una lista de las tablas de base de datos a las que accede más de un servicio están entre las vistas más informativas de una arquitectura, y todas pueden generarse automáticamente y seguirse en la compilación. La siguiente sección pasa al principio que mantiene débil el acoplamiento desde el principio: ocultar las decisiones tras interfaces.

Comprueba lo que has entendido

¿Por qué una función con un parámetro de modo que selecciona comportamientos completamente distintos es un ejemplo de acoplamiento de control?

Porque quien llama tiene que saber cómo está organizado por dentro quien es llamado y elegir su rama interna; cambiar la lógica de quien es llamado puede obligar a cambiar a quienes lo llaman.

Un módulo tiene Ca = 12 y Ce = 1. ¿Qué sugiere esto?

Muchos módulos dependen de él y él depende de poco, así que cambiarlo es arriesgado y los cambios deben ser escasos y cuidadosos; su interfaz merece una estabilidad especial.

Dos servicios se comunican solo mediante eventos, pero siempre deben lanzarse juntos. ¿Qué tipo de acoplamiento queda?

El acoplamiento de despliegue, normalmente causado por cambios no compatibles con versiones anteriores en el evento compartido o por acuerdos semánticos ocultos.

Indica un costo de sustituir una llamada directa por un evento.

Los resultados pasan a tener consistencia eventual en lugar de inmediata; además, ganas un broker que operar, un esquema de eventos que versionar y un flujo más difícil de rastrear.

Siguiente · 3.3Ocultación de informaciónPor qué los módulos deben ocultar decisiones, en qué se diferencia la ocultación de información del encapsulamiento, las interfaces como contratos y el diseño por contrato, las abstracciones con fugas, los módulos profundos, la ley de Hyrum y cómo mantener estables las API entre componentes.Continuar →

Fuentes y lecturas recomendadas

¿Tienes una pregunta sobre esta sección? ArchitectBot, el asistente de estudio con IA del curso, responde primero a partir del curso.

Pregunta: “Nuestros dos servicios comparten una tabla de base de datos. ¿Cómo los separo paso a paso sin una migración de golpe (big bang)?”