La arquitectura como carga

Durante mi carrera, trabajé con varios proyectos heredados, cada uno de los cuales sufrió varios defectos.


Por supuesto, a menudo el problema principal era la mala calidad del software (falta de pruebas unitarias, negativa a usar los principios de código limpio ...), pero también hubo dificultades, cuya fuente fueron las decisiones arquitectónicas tomadas al comienzo del proyecto o incluso durante el inicio del sistema corporativo. En mi opinión, esta clase de problemas es la causa del mayor dolor para muchos proyectos.


En esencia, mejorar el código es bastante sencillo, especialmente ahora que el movimiento de artesanía de software está promoviendo buenas prácticas de equipo. Sin embargo, cambiar las partes centrales de los sistemas, las restricciones impuestas al comienzo de su ciclo de vida, es una tarea extremadamente difícil.


Le contaré sobre varios tipos de soluciones arquitectónicas que he encontrado, que pueden convertirse en una carga real para los equipos involucrados en el soporte de dichos sistemas.



Toda su empresa comparte su base de datos.


Este es quizás uno de los problemas más comunes que he visto. Cuando varias aplicaciones necesitan usar datos compartidos, ¿por qué no les damos acceso compartido a una base de datos compartida? Después de todo, la duplicación se considera malvada en el desarrollo de software, ¿verdad? Bueno, esto no siempre es cierto, y especialmente cuando se trata de la base de datos. Venkat Subramaniam lo expresó así: "La base de datos es como un cepillo de dientes: nunca dejes que nadie la use". ¿Qué tiene de malo compartir una base de datos? De hecho, bastante ...


Lo primero que viene a la mente es el acoplamiento del modelo de datos. Imagine que dos aplicaciones, A y B, procesan datos en automóviles. El apéndice A es utilizado por el equipo responsable de los trabajos de reparación y, por lo tanto, necesitan almacenar mucha información técnica: sobre la mecánica, las averías, el historial de intervenciones en el automóvil ... El apéndice B se utiliza para establecer tareas para el equipo técnico, de modo que solo necesita información básica sobre el automóvil, suficiente para su identificación En este caso, usar la misma estructura de datos para ambos programas no tiene sentido: usan datos diferentes, por lo que cada uno debe usar su propia estructura de datos. Esto se hace más fácil porque el automóvil es fácil de identificar, por lo que no hay necesidad de una referencia general.


El segundo problema también se debe a esta malla del modelo de datos. Imagine que la aplicación B quiere cambiar el nombre del identificador del automóvil, darle un nombre más lógico en términos de su área temática. En este caso, la aplicación A también tendrá que actualizarse: el nombre de la columna ha cambiado ... Como resultado, para no molestar al equipo de la aplicación A, los desarrolladores B comenzarán a duplicar información en otra columna, ya que no pueden cambiar una columna existente ... Por supuesto, los desarrolladores A dirán que planean cambiar el nombre de la columna en el futuro para no almacenar la misma información en dos columnas, pero todos sabemos: lo más probable es que esto nunca se haga ...


¡Todo se vuelve aún más desagradable cuando las aplicaciones no solo leen datos de una fuente, sino que también los cambian! ¿Quién es el propietario de los datos en este caso? ¿En quién confiar? ¿Cómo se puede garantizar la integridad de los datos? Esto es difícil incluso cuando diferentes partes de la misma aplicación cambian los mismos datos, pero cuando varias aplicaciones diferentes lo hacen, el problema se vuelve mucho más grave ...


El último de los casos que he visto: dos aplicaciones que comparten la misma estructura de datos para almacenar información sobre dos objetos comerciales, relativamente similares, pero al mismo tiempo lo suficientemente diferentes como para complicar seriamente la comprensión de a qué aplicación se refieren los datos. Ambas aplicaciones utilizaron la misma tabla para modelar ejecuciones en el mercado financiero, pero con diferentes niveles de agregación. Nada indicaba que la tabla contiene datos de dos tipos, por lo que tuvimos que mirar otra tabla (perteneciente a la segunda aplicación) para determinar las filas generadas por cada uno de los programas ... Cada nuevo desarrollador que tuvo que trabajar con esta tabla inevitablemente vino en el mismo rastrillo que todos sus predecesores, y utilizó datos incorrectos (vulnerables), con todos los riesgos derivados de esto para la empresa.


Su sistema está vinculado al software utilizado en la empresa.


No todas las empresas pueden desarrollar software que satisfaga todas las necesidades de su negocio. De hecho, en muchos casos esto sería un invento de la bicicleta, ya que las necesidades de muchas compañías son las mismas, y es fácil encontrar software en el mercado que ya tenga la funcionalidad necesaria.


En general, a menudo es más barato comprar un producto que crearlo. Pero, por supuesto, el producto que acaba de comprar no funciona inmediatamente sin problemas con el software que ya utiliza, por lo que debe crear un puente entre las dos aplicaciones (en la mayoría de los casos, propietarias). Seguramente desarrollará sus propias herramientas para partes específicas de su negocio, y siempre que este costoso software que ya haya comprado tenga un modelo adecuado, simplemente tiene que usar su base de datos y agregar sus datos a las tablas de esta base de datos ...


Pasan varios años, una docena de desarrolladores o equipos realizan estas acciones una y otra vez, y luego se encuentra en un callejón sin salida: simplemente no puede usar otro software si el desarrollador del software que compró lo cierra, o deja de dar soporte al producto, o algún software nuevo resulta ser Se adapta mejor a sus necesidades. En algunos casos, incluso puede tener dependencias técnicas en software externo. Si el autor de la solución de software que está utilizando quiere que use la versión del lenguaje / marco / algo más que necesita, esto significa que la arquitectura de su sistema no le pertenece. Si desea vender una nueva versión para proporcionar una función que definitivamente necesita, pero esta versión está sujeta a un cambio en los requisitos técnicos, se verá obligado a actualizar su paquete de tecnología para cumplir con las recomendaciones de otras personas. Sé cómo se siente, y no querrías que esa migración forzada sea algo frecuente ...


Trabajé en un proyecto donde los desarrolladores del producto que utilizamos no querían agregar nuevas características para todos nuestros clientes, porque era demasiado difícil para ellos mantener cambios competitivos y varias versiones actuales (cada cliente tenía su propia versión con funciones necesarias solo para él). Entonces decidieron vendernos el SDK para que pudiéramos implementar nuestra propia funcionalidad. Naturalmente, no proporcionaron suficiente documentación sobre cómo hacer esto, y, además, nos vimos obligados a utilizar sus entidades comerciales, que tuvimos que descompilar para comprender su estructura, porque no teníamos ni el código fuente ni la documentación ... Implementación el funcional más simple tomó varios días, y era casi imposible de probar, ya que todo era demasiado complicado e incluso requería el conocimiento de un lenguaje de scripting que nadie en el equipo con una pila tecnológica ya compleja sabía ...


Malla fuerte entre múltiples aplicaciones


Recordemos a principios de la década de 2000: qué agradable fue usar Enterprise Java Beans (EJB) para manejar llamadas remotas entre aplicaciones en su sistema de información. En ese momento, esto podría haber parecido una buena idea. Compartir su código con otros equipos para evitar la duplicación también se ve bien. Sí, todos los equipos se vieron obligados a implementar sus aplicaciones al mismo tiempo para asegurarse de que las dependencias binarias no se rompieran, pero fue una noche divertida: comer pizza con colegas mientras esperaban que la aplicación se entregara durante 2 horas, ¿verdad?


Bueno, en realidad no fue tan divertido. Y la imposibilidad de refactorizar una clase separada en su propio código, simplemente porque a otra persona de la compañía le gustó su código y decidieron usarlo en su aplicación no probada, sigue siendo un placer.


Una vez que se da cuenta de lo confusas que son estas decisiones iniciales, el esfuerzo requerido para separar su aplicación del resto del mundo es enorme. Verá, en sentido literal, tomará años dividir su proyecto en varios componentes para que otras aplicaciones ya no puedan usar el núcleo de su área temática, su cliente o el mecanismo de almacenamiento en caché; eliminar todas las llamadas a clases externas que conducen a un fuerte vínculo con otros proyectos; para reemplazar todas las llamadas EJB con API REST ... Sin embargo, por todos estos esfuerzos, cada empleado asociado con el proyecto será más que recompensado: el desarrollo y las pruebas se simplificarán, el proceso de entrega se acelerará, ya que ahora ya no es necesario sincronizar su trabajo con otra persona. ; los conceptos en su propio código se separarán mejor; La administración de dependencias se simplificará, los problemas con las dependencias transitivas desaparecerán cuando importe un montón de dependencias de otras aplicaciones a su classpath ... ¡Todos estos cambios costosos solo salvarán la vida del equipo, y podrían ser mucho más fáciles de implementar si los hiciera al comienzo del proyecto!


Construyes tu proyecto sobre la base del proyecto de otra persona


Probablemente no encuentre este problema, pero aún puede suceder, y si eso sucede, entonces este es el peor de los casos, porque combina varios de los problemas discutidos anteriormente. En verdad, me encontré con uno similar en uno de los primeros proyectos de mi carrera profesional.


Cuando me uní al proyecto, me dijeron que el sistema corporativo se reescribió por completo y que el trabajo en el proyecto comenzó hace solo 2 meses. Imagine mi sorpresa cuando vi una aplicación web compleja con un módulo de administración completo, ya implementaba una funcionalidad empresarial compleja y un marco desarrollado para escribir otros módulos. Pronto descubrí que la mayoría de todo esto no fue escrito por mi equipo: para no comenzar desde cero, se decidió reutilizar el marco desarrollado por otra empresa de nuestro grupo. El problema era que este marco no estaba aislado del proyecto para el que fue desarrollado. Entonces, nuestro equipo simplemente recibió un archivo con todo el código fuente para el proyecto de otra compañía, incluida su lógica de negocios, que no tenía nada que ver con nuestro propio negocio. Para empeorar las cosas, también heredamos de ellos el esquema de la base de datos y los datos en sí ...


No fue fácil para un recién llegado al equipo comprender qué código se relaciona con el marco, qué se refiere a nuestro proyecto y qué se refiere al negocio de otra compañía. El equipo quería aclarar este desastre, pero varios intentos de hacerlo terminaron en serios errores de regresión debido a dependencias entre las partes del código (¡el lenguaje no se convierte en módulos, porque solo había un módulo!), Y, por supuesto, no hay pruebas automatizadas allí fue. Además, tuvimos que abandonar la idea de usar un servidor de aplicaciones diferente, ya que tenía un código específico utilizado por otra compañía en todo el sistema, y ​​esto hizo que la migración fuera demasiado costosa para nuestro pequeño equipo.


En algún momento, queríamos agregar algunas características agradables al marco, pero nos dijeron que esto ya se había hecho en otra compañía. Por lo tanto, se nos pidió que fusionáramos nuestra versión actual con la versión actual del código de otra compañía ... El equipo logró evitar esta pesadilla simplemente redirigiendo (selección de cereza) algunos de los compromisos asociados con la nueva función, pero aún era demasiado complicado y sofisticado en comparación con eso. lo que necesitábamos ...


Logramos terminar este proyecto, pero su calidad fue una verdadera molestia. Al menos el 40% del código y el contenido de la base de datos eran lastres no utilizados, y eliminar este código muerto no era una prioridad. Espero que el equipo finalmente haya tenido la oportunidad de separar su propio código. ¡No sé, dejé el equipo antes de que esto sucediera!


Toda la lógica de su negocio se almacena dentro de un sistema de gestión de reglas.


Poner parte de su lógica empresarial en un sistema de gestión de reglas es una práctica común. Esto, por ejemplo, es útil si algunas de sus reglas comerciales necesitan actualizarse con frecuencia, y el proceso de entrega de su aplicación monolítica requiere una larga fase de prueba antes de que pueda verificar el candidato de lanzamiento, y esto hace que sea imposible configurar algunos de sus "volátiles" "Reglas. Aunque prefiero un enfoque en el que todas las reglas del área temática se encuentren en el código fuente, puedo entender que en algunas situaciones un sistema de administración de reglas puede ser útil.


Sin embargo, me encontré con una aplicación donde casi TODA la lógica de negocios estaba en el sistema de gestión de reglas, ¡y a veces las reglas se generaban en base a un archivo Excel! Además, se suponía que las reglas no debían cambiarse muy a menudo, ya que el proyecto era, en general, un simple paquete ETL . El proyecto Java en el que se basó todo esto consistió solo en detalles técnicos sobre el marco de procesamiento por lotes y la lectura y escritura pura de los sistemas de origen y destino, sin ninguna conexión con el área temática.


Como resultado, todas las reglas fueron escritas en un lenguaje especial que nadie realmente conocía bien en el equipo, que era difícil de escribir (nuestros IDEs no lo admitían) y que era prácticamente imposible de depurar y probar. Cuando se solicitó una nueva regla o un cambio a una existente, la mayoría de los desarrolladores en los equipos simplemente copiaron la regla existente, lo que condujo a archivos completamente idénticos, con la excepción de un cambio específico (a menudo, el único cambio de ese tipo era el campo al que se aplicaba la regla).


Si esto ya parece un problema, entonces hay algo más: no había absolutamente ninguna pista sobre su propósito en ninguna regla. Las reglas fueron nombradas como Rule1, Rule2, y así sucesivamente, ¡con un total de más de 100! Y, básicamente, cada regla consistía en verificaciones y asignación de valores codificados sin ningún término de dominio. Incluso el nombre del proyecto no aclaraba el propósito de todo el ETL en su conjunto.


Conclusión


Como explicó el tío Bob en su libro "Arquitectura limpia", al pensar en la arquitectura de su proyecto, algunas decisiones deben posponerse hasta que realmente necesitemos tomar una decisión, sin lo cual no podemos continuar agregando valor a nuestro producto (como elegir una base de datos por ejemplo) Otras decisiones deben tomarse muy temprano, no espere hasta que las cosas se pongan mal. Afortunadamente, este tipo de decisiones críticas es fácil de identificar porque son lo que se puede llamar "arquitectura con alma": cuando piensas en esas ideas, no ves nada bueno en ellas, solo un problema que tarde o temprano volverá y te persiguen Desafortunadamente, cuando se trabaja con proyectos heredados, este tipo de carga a menudo está enterrada en el código, lo que hace que sea muy costoso eliminarlo.


Pero no debemos tener miedo. Sí, limpiar el desorden que se ha acumulado a lo largo de los años e incluso décadas no es una tarea fácil, pero como desarrolladores de software profesionales simplemente no podemos permitir que tales problemas continúen pudriéndose y matando la motivación de los desarrolladores, la confianza de los clientes y nuestra capacidad de beneficiarlos, integrados en nuestro producto.


Por supuesto, todo tipo de carga arquitectónica que describí se puede eliminar de varias maneras: no hay una bala de plata para resolver ninguno de estos problemas. Sin embargo, estoy seguro de que cualquier equipo puede llegar a algo para finalmente deshacerse de esta carga. ¡Así que enfrentemos nuestros problemas juntos y comencemos a poner las cosas en orden!

Source: https://habr.com/ru/post/es427591/


All Articles