miércoles, 8 de enero de 2020

Capítulo 16: Independencia - Parte 2 - Clean Architecture


Habilidad de desarrollo independiente
Cuando los componentes están fuertemente desacoplados, se mitiga la interferencia entre los equipos. Si las reglas de negocio no conocen la UI, entonces un equipo que se enfoca en la UI no puede afectar mucho a un equipo que se enfoca en las reglas de negocio.Si los propios casos de uso están desacoplados entre sí, es poco probable que un equipo que se centre en el caso de uso de crear un pedido se enfoque en el caso de uso de borrar un pedido.

Siempre que las capas y los casos de uso se desacoplen, la arquitectura del sistema apoyará la organización de los equipos, independientemente de si están organizados como equipos de características, equipos de componentes, equipos de capas o alguna otra variación.
Despliegue independiente
El desacoplamiento de los casos de uso y las capas también ofrece un alto grado de flexibilidad en la implementación.De hecho, si el desacoplamiento se realiza bien, entonces debería ser posible cambiar capas y casos de uso en sistemas en ejecución. Agregar un nuevo caso de uso podría ser tan simple como agregar algunos nuevos archivos jar o servicios al sistema y dejar el resto solo.
Duplicación
Los arquitectos a menudo caen en una trampa, una trampa que depende de su miedo a la duplicación.

La duplicación es generalmente una mala cosa en el software. No nos gusta el código duplicado. Cuando el código está realmente duplicado, estamos obligados como profesionales a reducirlo y eliminarlo.

Pero hay diferentes tipos de duplicación. Hay una verdadera duplicación, en la que cada cambio en una instancia requiere el mismo cambio en cada duplicado de esa instancia. Entonces hay duplicación falsa o accidental, si dos secciones del código aparentemente duplicadas evolucionan a lo largo de diferentes caminos, si cambian a diferentes velocidades y por diferentes razones, entonces no son verdaderos duplicados. Si volvemos a ellos en unos años se verá que son muy diferentes entre sí.

Ahora imagine dos casos de uso que tienen estructuras de pantalla muy similares. Los arquitectos probablemente estarán tentados a compartir el código para esa estructura. Pero deberían hacerlo? ¿Es eso una verdadera duplicación? ¿O es accidental?

Lo más probable es que sea accidental. A medida que pasa el tiempo, lo más probable es que esas dos pantallas divergan y eventualmente se vean muy diferentes. Por este motivo, se debe tener cuidado para evitar su unificación. De lo contrario, separarlos más tarde será todo un desafío.

Cuando esté separando verticalmente los casos de uso entre sí, se encontrará con este problema, y ​​su tentación será unir los casos de uso porque tienen estructuras de pantalla similares, algoritmos similares, consultas de bases de datos y / o esquemas similares. Resista la tentación de cometer el pecado de la eliminación automática de la duplicación. Asegúrese de que la duplicación es real.

De la misma manera, cuando se están separando las capas horizontalmente, es posible que observe que la estructura de datos de un registro de base de datos particular es muy similar a la de los datos. Se puede tener la tentación de simplemente pasar el registro de la base de datos hast ala interfaz de usuario, en lugar de crear un modelo vista que se vea igual y copie los elementos. Hay que tener cuidado: esta duplicación es casi seguramente accidental. Crear el modelo de vista separado no es un gran esfuerzo, y ayudará a mantener las capas desacopladas correctamente.
Desacoplando modos (de nuevo)
De vuelta a los modos. Hay muchas formas de desacoplar capas y casos de uso. Se pueden desacoplar en el nivel del código fuente, en el nivel del código binario (implementación) y en el nivel de la unidad de ejecución (servicio).
Nivel de código
Podemos controlar las dependencias entre los módulos de código fuente para que los cambios en un módulo no obliguen a los cambios o la compilación de otros (por ejemplo, Ruby Gems).

En este modo de desacoplamiento, todos los componentes se ejecutan en el mismo espacio de direcciones y se comunican entre sí mediante llamadas de función simple. Hay un solo ejecutable cargado en la memoria de la computadora. Popularmente se conoce como una estructura monolítica.
Nivel de despliegue
Podemos controlar las dependencias entre unidades desplegables, como archivos jar, DLL o bibliotecas compartidas, para que los cambios en el código fuente en un módulo no obliguen a otros a reconstruirse y volver a implementarse.

Muchos de los componentes aún pueden vivir en el mismo espacio de direcciones y comunicarse a través de llamadas de función. Otros componentes pueden vivir en otros procesos en el mismo procesador y comunicarse a través de comunicaciones entre procesos, sockets o memoria compartida. Lo importante aquí es que los componentes desacoplados se dividen en unidades de implementación independiente, como archivos jar, archivos Gem o DLL.
Nivel de servicio
Podemos reducir las dependencias hasta el nivel de las estructuras de datos y comunicarnos únicamente a través de paquetes de red, de modo que cada unidad de ejecución sea totalmente independiente de los cambios de fuente y binarios a otros (por ejemplo, servicios o microservicios).
¿Cuál es el mejor modo para usar?
La respuesta es que es difícil saber qué modo es el mejor durante las fases iniciales de un proyecto. De hecho, a medida que el proyecto madura, el modo óptimo puede cambiar.

Por ejemplo, no es difícil imaginar que un sistema que se ejecute cómodamente en un servidor en este momento pueda crecer hasta el punto en que algunos de sus componentes deban ejecutarse en servidores separados. Mientras que el sistema se ejecuta en un solo servidor, el desacoplamiento a nivel de origen podría ser suficiente. SIn embargo, más tarde, podría requerir el desacoplamiento en unidades desplegables o incluso servicios.

Una solución (que parece ser popular en este momento) es simplemente desacoplar en el nivel de servicio de forma predeterminada. Un problema con este enfoque es que es caro y alienta el desacoplamiento de grano grueso. No importa cuán “micro” sean los microservicios, es probable que el desacoplamiento no sea lo suficientemente detallado.

Otro problema con el desacoplamiento a nivel de servicio es que es costoso, tanto en tiempo de desarrollo como en recursos del sistema. Tratar con los límites del servicio donde no se necesitan es un desperdicio de esfuerzo, memoria y ciclos. Y, sí, sé que los dos últimos son baratos, pero el esfuerzo no lo es.

Se recomienda empujar el desacoplamiento hasta el punto donde se podría formar un servicio,  si fuera necesario, pero luego dejar los componentes en el mismo espacio de direcciones el mayor tiempo posible. Esto deja abierta la opción para un servicio.

Con este enfoque, inicialmente los componentes se separan en el nivel del código fuente. Eso puede ser lo suficientemente bueno para la duración del proyecto. Sin embargo, si surgen problemas de implementación o desarrollo, puede ser suficiente llevar un poco de desacoplamiento a un nivel de implementación, al menos por un tiempo.

A medida que aumentan los problemas de desarrollo, implementación y operación, se elije  cuidadosamente qué unidades desplegables se convertirán en servicios y gradualmente cambiaré el sistema en esa dirección.

Con el tiempo, las necesidades operativas del sistema pueden disminuir. Lo que una vez requirió desacoplamiento en el nivel de servicio ahora puede requerir solo desacoplamiento a nivel de implementación o incluso a nivel de fuente.

Una buena arquitectura permitirá que un sistema nazca como un monolito, implementado en un solo archivo, pero luego se convierta en un conjunto de unidades desplegables independientemente, y luego a servicios independientes y / o microservicios. Más tarde, a medida que cambian las cosas, debería permitir revertir esa progresión y deslizarse hacia abajo en un monolito.

Una buena arquitectura protege a la mayoría del código fuente de esos cambios. Deja el modo de desacoplamiento abierto como una opción para que las implementaciones grandes puedan usar un modo, mientras que pequeños despliegues pueden usar otro.
Conclusión
Sí, esto es complicado. Y no estoy diciendo que el cambio de los modos de desacoplamiento deba ser una opción de configuración trivial (aunque a veces eso es apropiado). Lo que estoy diciendo es que el modo de desacoplamiento de un sistema es una de esas cosas que probablemente cambiará con el tiempo, y un buen arquitecto prevé y facilita apropiadamente esos cambios.

No hay comentarios:

Publicar un comentario