El problema kitty
Como un ejemplo de estas dos falacias, veremos nuestro sistema de agregador de taxi de
nuevo. Recuerda, el sistema sabe cuántos proveedores de taxi hay en una ciudad
dada y permite a los clientes pedir carreras. Se asume que los clientes
seleccionan los taxis basados en un número de criterios como el tiempo de
recogida, el coste, el lujo y la experiencia del conductor.
Se quiere que el sistema sea escalable así que nosotros elegimos construir este sistema a
partir de muchos microservicios. Subdividimos a nuestro personal de desarrollo
en muchos equipos pequeños, cada uno de los cuales es responsable de
desarrollar, mantener y operar un número correspondientemente pequeño de
servicios.
El siguiente diagrama muestra cómo nuestros arquitectos ficticios ordenaron los
servicios para implementar esta aplicación. El servicio «TaxiUI» trata con el
cliente, quién usa el dispositivo móvil para pedir taxis. El servicio
«TaxiFinder» examina los inventarios de varios «TaxiSuppliers» y determina que
taxis son los posibles candidatos para el usuario. Estos se depositan en un
registro de datos a corto plazo adjunto a ese usuario. El servicio
«TaxiSelector» toma los criterios del usuario y elige un taxi apropiado entre
los candidatos. Esto saca al taxi fuera del servicio «TaxiDispatcher» el cual
ordena el taxi apropiado.
Ahora supongamos que este sistema ha estado operativo desde hace más de un año.
Nuestro equipo de desarrollo ha estado desarrollando alegremente nuevas
características mientras mantenía y operaba todos estos servicios.
Un día el departamento de marketing mantiene una reunión con el equipo de desarrollo. En
esta reunión, ellos anuncian sus planes para ofrecer un servicio de entrega de
gatitos a la ciudad. Los usuarios pueden ordenar que los gatitos sean
entregados en sus hogares o en sus lugares de negocios.
La compañía establecerá varios puntos de recogida de gatitos a través de la ciudad. Cuando
se hace un pedido de un gatito, se seleccionará el taxi más cercano para
recoger al gatito de uno de aquellos puntos de recogida y entonces lo entregará
en la dirección apropiada.
Uno de los proveedores de taxi está de acuerdo en participar, otros probablemente lo sigan
pero otros puede declinar el acuerdo.
Cabe la posibilidad de que algunos conductores sean alérgicos a los gatos por lo que
estos conductores no deberían ser seleccionados para este servicio. Los
clientes también pueden ser alérgicos a los gatos por lo que un vehículo que ha
estado en uso para entregar gatitos en los últimos tres días no se debería
seleccionar para los clientes quienes hayan manifestado alergias.
Mirando al diagrama de servicios. ¿Cuantos servicios cambiarán para implementar esta
funcionalidad? Pues todos ellos. Claramente, el desarrollo y despliegue de la
funcionalidad gatito tendrá que estar coordinada cuidadosamente.
En otras palabras, los servicios está todos acoplados y no se pueden desarrollar,
desplegar ni mantener independientemente.
Este es el problema con las aspectos transversales. Cada sistema software debe encarar
este problema, ya sea orientado el servicio o no.
Las descomposiciones funcionales, del tipo representado en el diagrama de servicio
en la Figura 27.1, son muy vulnerables a las nuevas características que
atraviesan todos esos comportamientos funcionales.
Objetos al rescate
¿Cómo deberiamos haber solventado ese problema en una arquitectura basada en
componentes? Tomando en consideración los principios de diseño SOLID nos habría
impulsado a crear un conjunto de clases que podrían extenderse polimórficamente
para manejar estas nuevas características .
El siguiente diagrama muestra la estrategia. Las clases en este diagrama
corresponden aproximadamente a los servicios mostrados en la figura anterior.
Sin embargo, hay que tener en cuenta los límites. Las dependencias siguen la
regla de la dependencia.
Mucha de la lógica de los servicios originales se preserva dentro de las clases base del
objeto modelo. Sin embargo, ese proporción de la lógica que fue específica a
las carreras se ha extraído de un componente «Rides». La nueva
característica para los gatitos se ha colocado en el componente
«Kittens». Estos dos componente sobreescriben la clases abstractas base en el
componente original usando un patrón como «Template Method» or «Strategy».
Hay que tener en cuenta que los dos nuevos componentes, «Rides» y «Kittens» siguen la
regla de la dependencia. Tenga en cuenta que las clases que implementan
aquellas características están creadas por factorías bajo el control de la UI.
Claramente, en este escenario, cuando la característica Kitty se implementa, «TaxiUI» debe
cambiar pero no es necesario cambiar nada más. Más bien, un nuevo archivo jar,
o gema o DDL se añade al sistema y dinámicamente se carga en tiempo de
ejecución.
De esta manera, la característica Kitty se desacopla es desarrollable y desplegable
independientemente.
Servicios basados en componente
La cuestión más obvia es: ¿podemos hacer esto con servicios? Y la respuesta es ¡por
supuesto! Los servicios no necesitan ser pequeños monolitos. Servicios pueden
estar diseñados usando principios SOLID y tener una estructura de componentes
de manera que los nuevos componentes se pueden añadir sin cambiar los
componentes existentes en el servicio.
Piensa en un servicio en Java como un conjunto de clases abstractas en uno o más ficheros
ja. Piensa en cada característica o ampliación de característica como otro
fichero jar que contiene clases que extienden clases abstractas en los primeros
archivos jar. Desplegar una nueva característica no implica redesplegar los
servicios si no en una manera de simplemente añadir los nuevos ficheros jar a
los paths de carga de aquellos servicios. En otras palabras, añadir nuevas
características conforma al principio «Open-Closed».
El diagrama de servicio de la siguiente figura muestra la estructura.
Los servicios todavía existen como antes pero cada uno tiene su propio diseño de
componente interno permitiendo a las nuevas características que sean
añadidas como nuevas clases derivadas. Aquellas clases derivadas viven dentro
de sus propios componentes
Preocupaciones transversales.
Lo que hemos aprendido es que los límites arquitectónicos no caen entre servicios. Por
el contrario, aquellos límites se ejecutan a través de servicios dividiendo
estos en componentes.
Para tratar con las preocupaciones transversales que todos los sistemas significativos
encaran, los servicios se deben diseñar con arquitecturas de componentes
internos que siguen la regla de la dependencia, como se muestra en el siguiente
diagrama.
Aquellos servicios no definen los límites arquitectónicos del sistema, en vez, los
componentes dentro de los servicios lo hacen.
Conclusión
Tan útiles como son los servicios para la escalabilidad y la desarrollabilidad de un
sistema, ellos no son en sí mismos elementos arquitectónicos significativos. La
arquitectura de un sistema se define por los límites dibujados en ese sistema y
por las dependencias que cruzan aquellos límites. Esa arquitectura no está
definida por los mecanismos físicos por los cuales los elementos se comunican y
ejecutan.
Un servicio podría ser un único componente, rodeado completamente por un límite
arquitectónico. Alternativamente, un servicio podría estar compuesto de varios
componentes separados por límites arquitectónicos. En casos raros, los clientes
y los servicios pueden estar tan acoplados como para no tener ningún
significado arquitectónico.
No hay comentarios:
Publicar un comentario