Cómo prevenir efectos dominó en software

Clase 34 de 43Curso Profesional de Arquitectura de Software

Resumen

Diseñar sistemas mantenibles exige reducir el riesgo de efectos dominó. Cuando un cambio menor obliga a modificar muchos módulos, el costo y la fragilidad se disparan. Aquí se explican tácticas clave para aislar cambios: ocultar información, mantener interfaces estables, restringir comunicación siguiendo la ley de Demeter, usar intermediarios y diferir el enlace mediante registro en ejecución, archivos de configuración, polimorfismo y reemplazo de componentes.

¿Cómo prevenir efectos dominó en la mantenibilidad?

Un efecto dominó ocurre cuando modificar el módulo A implica cambiar B, luego C, y así sucesivamente. Para evitarlo, se priorizan interfaces claras y estabilidad externa aunque cambie la implementación interna.

  • Ocultar información: encapsular datos y exponer una interfaz estable. Así, si cambia la información interna, los consumidores no cambian porque dependen de la interfaz, no de detalles internos.
  • Mantener la interfaz: consolidar acciones en un solo mensaje coherente. Evitar múltiples llamados para una misma operación que generen dependencias implícitas a pasos intermedios.
  • Ejemplo práctico: en un servicio que calcula un impuesto, aplica un beneficio y registra la compra, una única interfaz que orquesta la operación protege a los consumidores ante cambios en el cálculo o el orden de pasos.
  • Tipo de cambios: estas tácticas cubren cambios sintácticos o de secuencia. Los cambios semánticos (por ejemplo, pasar de usuario/contraseña a autenticación con redes sociales) agregan nuevos conceptos y pueden requerir modificar todo el módulo involucrado.

¿Por qué restringir comunicación mejora el acoplamiento?

Cuando un módulo habla con dependencias de sus dependencias, se vuelve frágil. Cualquier ajuste en la cadena impacta al originador del pedido.

  • Ley de Demeter (principio de menor conocimiento): hablar solo con dependencias directas. Reducir conocimiento del grafo completo de objetos.
  • Ejemplo de orden de compra: en lugar de pedir a la orden sus ítems, luego a los ítems sus productos y luego al producto su vendedor, pedir directamente a la orden la lista de vendedores involucrados. Menos saltos, menos riesgo.
  • Intermediarios que estabilizan: usar un punto de compatibilidad que absorba cambios de interfaz o formatos de mensajes. Patrones como facade, proxy, adapter o incluso factory reducen acoplamiento entre clientes e implementaciones. A nivel arquitectónico, conectores como publish subscribe, repositorio o blackboard desacoplan componentes para que, si algo cambia, solo se ajuste el conector.

¿Cuándo diferir el enlace acelera el despliegue?

Enlace en compilación obliga a redeploys conjuntos. Diferir el enlace a ejecución o a carga del sistema permite desplegar servicios de forma independiente y mejora la mantenibilidad.

¿Qué implica registro en ejecución?

  • Registro de servicios en tiempo de ejecución: un servicio se vuelve disponible para sus dependencias al iniciar el sistema. Se compila contra una interfaz y se posterga la decisión de implementación.
  • Estrategias como ejemplo: decidir en ejecución si escribir a archivo o a un almacenamiento remoto, seleccionado por configuración y registro dinámico.

¿Cómo ayudan configuración e inyección de dependencias?

  • Archivos de configuración: práctica común en desarrollo web para decidir cómo conectar módulos. Cada conector entre módulos es una interfaz; la implementación concreta se asigna al iniciar.
  • Arquitectura de plug-ins: para abrir un archivo de tipo JavaScript en un IDE, se busca en configuración si existe un plug-in para .JS y se instancia el servicio correspondiente.

¿Dónde aplica polimorfismo y reemplazo de componentes?

  • Polimorfismo: un objeto adapta su comportamiento según su estado. Permite diferir la resolución del comportamiento a la instancia concreta. El patrón de diseño state es un caso icónico.
  • Reemplazo de componentes: desplegar una nueva versión que respete la misma interfaz. El enlace se resuelve en la carga, re-registrando la nueva instancia sin afectar a los consumidores.
  • Adherir a protocolos: definir esquemas de comunicación independientes de instancias concretas. Mensajes serializados y deserializados con un esquema claro (por ejemplo, JSON o XML) permiten desplegar módulos de forma independiente mientras ambos se ajusten al mismo protocolo.

¿Te gustaría compartir cómo aplicas ocultar información, ley de Demeter, intermediarios o enlace diferido en tus proyectos? Comenta tus casos y retos.