Modularity

Propiedad de unidades y relaciones

Modularity describe qué tan bien está estructurado un sistema en unidades que encapsulan responsabilidades y decisiones de diseño, y colaboran mediante interacciones deliberadamente controladas. Es una propiedad de la estructura y sus relaciones. Tener carpetas, proyectos, assemblies o servicios no la demuestra.

En Commerce, Orders puede solicitar autorización mediante la superficie pública de Payments, mientras Payments protege su estado y detalles del proveedor. Esa colaboración permite entender quién decide y qué conocimiento necesita cada participante. La modularidad debe contrastarse con accesos reales y escenarios de cambio.

Usamos esta orientación como convención del Atlas. Module, component, cohesion y coupling tienen alcances distintos según fuente y vista. No proponemos una medida universal ni exigimos que cada unidad corresponda a un mecanismo particular de lenguaje o ejecución.

Decomposition, Components y Boundaries

decomposition establece unidades y criterios de división. Modularity examina si las unidades resultantes conservan encapsulación y colaboración controlada. Un sistema descompuesto no es automáticamente modular.

Crear Orders/ y Payments/ divide código. Si Orders escribe tablas privadas de Payments, Payments importa internals de Orders y cambios locales propagan detalles entre ambos, la partición física no protege las responsabilidades declaradas. El problema persiste aunque existan dos proyectos compilables.

components permite representar elementos significativos de una vista. Tener muchos components no implica buena modularidad. boundaries aclara dónde cambian responsabilidad, autoridad o reglas; una boundary también puede exponer demasiado o permitir accesos que la evadan.

Estas preguntas se complementan: qué unidades existen, qué límites deberían conservar y qué decisiones realmente ocultan. Nombrar las unidades es el inicio del razonamiento, no evidencia suficiente de sus propiedades.

Modularity y Granularity

granularity compara qué tan gruesas o finas son las unidades relevantes. Modularity compara cómo encapsulan y colaboran. Una unidad amplia puede tener límites claros, y muchas unidades pequeñas pueden depender continuamente de representaciones privadas.

En Commerce, Payments como módulo único puede proteger autorización, captura y reembolso frente a Orders. Subdividirlo en Authorization, Capture y Refund puede también conservar buenos límites si sus contratos y autoridades son significativos. Los drivers deben justificar el costo de esa granularidad adicional.

No hay que convertir «modular» en sinónimo de fine-grained. Tampoco un conteo de módulos o servicios permite comparar encapsulación entre sistemas. Interesa el significado de sus responsabilidades y relaciones, además de su cantidad.

Information hiding como criterio central

Parnas propone asignar a módulos decisiones de diseño que deben ocultarse a otras partes, especialmente aquellas difíciles o susceptibles de cambiar. La interface revela lo necesario para colaborar, sin exponer innecesariamente cómo se realiza. Su módulo es una asignación de responsabilidad, no necesariamente un subprograma. On the Criteria To Be Used in Decomposing Systems into Modules, 1972.

En nuestro ejemplo, Payments interpreta códigos privados del proveedor y ofrece resultados con significado para Commerce. Orders depende de la capacidad y estados acordados, no de tablas de traducción o secuencias internas de la integración.

Si cambiar el proveedor exige modificar Orders porque allí se interpretan esos códigos, la decisión no estaba realmente oculta. El diseño puede admitir esa dependencia por un motivo explícito, pero no debería prometer independencia frente a ese cambio.

Ocultar no significa prohibir diagnóstico ni excluir toda información. Hay que exponer resultados, restricciones y evidencia que los consumidores necesitan. Una superficie opaca que obliga a adivinar comportamiento no mejora modularidad por tener menos métodos. essential-complexity distingue las obligaciones que permanecen; accidental-complexity explica cómo localizar protocolos o traducciones puede reducir exposición sin eliminar su implementación.

Cohesion: relaciones dentro de una unidad

Cohesion examina cuánto se relacionan las responsabilidades de una unidad. Agrupar obligaciones y motivos de cambio coherentes puede facilitar comprensión y localidad de modificaciones. No implica que todo elemento del módulo deba cambiar siempre junto.

El SEI utiliza cohesion y coupling para justificar tácticas de modifiability y exige precisar qué modificaciones buscamos favorecer. Su tratamiento vincula relaciones entre responsabilidades con consecuencias de cambio; aquí usamos ese razonamiento sin adoptar un score ni una clasificación mecánica. Modifiability Tactics, secciones 3 y 6.

Autorizar y capturar pueden compartir reglas comerciales importantes y evolucionar a ritmos diferentes. Ese dato invita a investigar qué conviene conservar junto; no decide automáticamente un nuevo servicio. Dos responsabilidades que usan la misma biblioteca no son por ello una única responsabilidad arquitectónica.

Un módulo llamado Common puede reunir funcionalidades sin relación suficiente. Separarlas tampoco debe ignorar invariantes o colaboración legítima. La cohesión se examina respecto de la preocupación y del cambio considerado.

Coupling: relaciones entre unidades

Coupling incluye dependencias de código, contratos, schemas, disponibilidad de runtime, supuestos temporales, delivery y procesos organizacionales. dependency-structure permite observar esas relaciones sin confundir un import con toda dependencia arquitectónica.

Orders puede dejar de importar una clase concreta y seguir necesitando que Payments responda durante la confirmación. También puede usar una interface pública y depender de su significado. Eliminar una referencia de código no elimina necesariamente dependencia temporal o de contrato.

El objetivo no es cero dependencias. Sistemas útiles colaboran. Interesan dependencias intencionales, comprensibles, acotadas y suficientemente estables para las necesidades actuales. Un dibujo con pocas flechas puede esconder coordinación mediante datos, configuración o releases.

Interfaces y dirección de dependencias

interfaces describe lo expuesto deliberadamente. Una interface útil ofrece capacidades que el consumidor necesita y evita trasladarle decisiones privadas. Keyword interface, API o dependency injection no garantizan esa calidad.

En un ejemplo, Application conoce un Port significativo y el Adapter depende también de ese Port para implementarlo. El ensamblaje suministra la realización. Esa dirección puede proteger el núcleo de detalles concretos, mientras conserva obligaciones operacionales.

ports-and-adapters desarrolla esa organización, sin constituir la definición de Modularity. Crear carpetas hexagonales no prueba que los contratos estén libres de tipos del proveedor ni que existan rutas de acceso controladas.

Una interface demasiado genérica puede exigir flags, estados internos y secuencias ajenas. La indirection cambia de forma sin ocultar la decisión relevante. Conviene revisar qué conocimiento necesita realmente el consumidor para usarla correctamente.

Encapsulation y mecanismos de protección

Encapsulation controla acceso a decisiones y estado internos. Puede sostenerse mediante reglas del lenguaje, módulos, APIs públicas, procesos, permisos de almacenamiento, convenciones y verificaciones. La protección elegida necesita corresponder a la regla relevante.

Una convención puede ser suficiente en cierto contexto y permitir bypass en otro. Un compilador puede impedir imports privados sin contener consumo de memoria. Un proceso separado puede restringir acceso directo y agregar comunicación, observación y recuperación.

En su discusión de Design Rules, Lenfle y Baldwin relacionan modularidad con partes que pueden diseñarse o usarse con cierta independencia y reglas compartidas de interfaces e integración. Esa independencia depende de acuerdos explícitos; no equivale a ausencia de colaboración. HBS, sección 5.2.

Enforcement más fuerte introduce costos y fricción. La decisión requiere riesgo, drivers y capacidad del equipo. Encapsular todo al máximo puede restringir cambios legítimos o multiplicar traducciones sin beneficio suficiente.

Commerce: mismo corte, distinta modularidad

AspectoVariante AVariante B
Unidades aparentesOrders y Payments.Orders y Payments.
ColaboraciónOrders usa una capacidad pública de Payments.Cada parte importa clases privadas de la otra.
Estado autoritativoPayments controla las escrituras de pago.Orders modifica tablas privadas de Payments.
Cambio del proveedorSe revisa Payments y el contrato cuando corresponda.Se propagan detalles internos a consumidores.

La cantidad y el alcance aparente de unidades pueden ser iguales. La calidad de sus límites e interacciones difiere. La variante A tampoco garantiza que todo cambio quede local: una nueva regla comercial transversal puede requerir trabajo coordinado legítimo.

Separar Payments en Authorization, Capture, Refund y Settlement admite otra granularidad con buena o mala modularidad. El análisis debe recuperar qué oculta cada parte y qué compromisos atraviesan sus límites, no premiar el número de cajas.

Datos, deployment y profiles

data-ownership aclara autoridad sobre estado. Compartir un servidor de base de datos no vuelve imposible la modularidad si accesos y contratos preservan esa autoridad. Leer una representación deliberadamente publicada difiere de consultar o modificar un schema privado sin compromiso de compatibilidad.

Bases separadas tampoco garantizan límites saludables. Un servicio puede filtrar internals, exigir cambios coordinados o reproducir reglas privadas ajenas mediante APIs. La separación de almacenamiento necesita acompañarse de responsabilidades y contratos coherentes.

modular-monolith busca límites lógicos efectivos conservando deployment y runtime compartidos. El nombre no prueba resultados: observar acceso a estado, dependencias permitidas y propagación del cambio. microservices puede fortalecer algunos límites físicos y conservar coupling mediante colaboración excesiva o releases coordinados.

Modularity no implica entrega independiente, ni unidades desplegadas por separado demuestran modularidad. communication puede usar llamadas locales, eventos, mensajes o HTTP; la tecnología no determina cómo se ocultan decisiones. Publicar eventos no elimina automáticamente coupling semántico.

architectural-quantum [ADVANCED] examina un alcance especializado de deployment, cohesión y coupling. Varios módulos bien encapsulados pueden participar en un único quantum; esa unidad operacional no sustituye evaluar modularidad interna.

Estructura interna y evolución

layered-architecture puede organizar responsabilidades técnicas mediante abstracciones y dependencias permitidas. Accesos entre capas que evaden esas reglas debilitan la estructura. Layering es una aproximación posible, no sinónimo de Modularity.

internal-structure organiza alternativas dentro de una frontera. architecture-profile reúne decisiones más amplias sobre el sistema. Modularity examina propiedades de unidades y relaciones en el alcance elegido; no reemplaza ninguna de esas preguntas.

modifiability evalúa costo y propagación de un cambio. evolvability examina adaptación sostenida. Límites significativos pueden conservar opciones, pero contratos, versionado, modelos traducidos y pruebas también tienen costo. Abstracciones prematuras pueden fijar supuestos incorrectos en lugar de facilitar cambios.

testability permite evaluar si interfaces controlables producen evidencia útil. Separar responsabilidades puede ayudar a preparar escenarios; mockear cada interacción diminuta puede ocultar incompatibilidades que solo aparecen al integrar. La modularidad no sustituye verificar colaboración real.

Erosión y revisión

architecture-erosion evalúa debilitamiento respecto de intención vigente, no toda diferencia con un diagrama antiguo. fitness-functions puede aportar feedback sobre dependencias y accesos representados; no observa automáticamente todos los usos de estado. evolutionary-architecture puede apoyarse en modularidad para orientar cambios, pero una estructura modular no demuestra por sí sola ese enfoque.

Imports internos, estado mutable compartido, reglas comerciales duplicadas, interfaces evadidas o ciclos que contradicen dependencias acordadas pueden debilitar límites con el tiempo. Un shortcut excepcional puede volverse la ruta habitual si nadie revisa sus consecuencias.

code-partitioning-strategies puede reflejar esos límites en archivos y proyectos, pero la organización física no los demuestra. Revisar qué parte cambia ante una modificación del proveedor, quién escribe estado privado y qué rutas cruzan la boundary.

La evidencia debe comparar intención con implementación y operación. Una estructura modular conserva colaboración útil con decisiones internas protegidas y compromisos explícitos; no se define por ausencia de flechas, separación física máxima o multiplicación de módulos.