Principios de software y necesidades de negocio

[English version]Este enlace se abrirá en una ventana nueva

2021_07_15_devops032021_07_15_devops03

Uno de los aspectos que más me preocupa no es sólo el cómo hacemos las cosas sino el porqué y, esa, es una cuestión bastante más complicada de lo que parece a simple vista. A fin de cuentas, podemos hacer algo porque es la forma en la que nos enseñaron, porque se hace así en el sector en el que trabajamos o incluso porque esta de moda. Todo ello son respuestas válidas, hasta cierto punto, pero, en realidad, no nos enfocamos el problema real. Lo que hacemos es para permitir que otro logre sus objetivos de negocio. Por tanto, la respuesta debería ser hacemos lo que hacemos para resolver un problema de negocio.

La pregunta, y su respuesta, son comunes a varios sectores y el del software, lógicamente, es uno de ellos. A decir verdad estamos tan acostumbrados a hablar de principios SOLID, de testing y de tantas otras cosas que se nos olvida que sólo lo hacemos desde un punto de vista técnico. Pero, ¿nos hemos planteado alguna vez como una arquitectura software puede ayudar al negocio a obtener sus objetivos? A fin de proporcionaros un ejemplo me voy a centrar en dos tipos de negocio diferentes.

  • Desarrollamos productos orientados a negocio (en modalidad empresa-empresa o a cliente): Tener un producto exitoso requiere que pueda ser adaptado a diferentes necesidades. Por tanto, tenemos que garantizar que pueda ser personalizado y extendido, permitiendo la incorporación de funcionalidades no previstas y que solo tienen sentido para un único cliente, de tal forma que la generalización no es posible.
  • Desarrollamos producto orientado al usuario final: En este caso, tu producto se tendrá que adaptar a nuevas necesidades de forma rápida, soportando distintas estrategias de marketing como puede ser marketing A/B u otras. De esta forma, tu producto tendrá que tener esta habilidad para ser personalizado, para ir en una dirección y virar rápidamente en otra o incluso volver atrás, todo ello minimizando el coste asociado.

Además, se debe tener en cuenta que cuando desarrollamos software siempre desarrollamos producto, incluso si trabajamos para una consultora o una software factory. A fin de cuentas, aunque no sea tu producto si lo es el de tu cliente.

Así pues, el software debe desarrollarse de una forma en la que se puedan garantizar estas necesidades, y que, como bien conocéis, se engloban bajo el acrónimo SOLID. Éste aunque incluye muchos principios e ideas, aunque si nos centramos de forma exclusiva en el negocio yo destacaría aquellas relacionadas con la extensibilidad y la dependencia en abstracciones.

En realidad, ambas características están íntimamente ligadas, tanto en el caso del backend como en el del frontend, si bien la forma de hacerlo y lo claro que sea la detección del mismo van a depender enormemente del lenguaje y del framework empleado.

Punto de vista técnico

Si nos centramos en el aspecto técnico, la cuestión no es, por tanto, si usamos anotaciones, Spring Boot IoC, Angular IoC o la tecnología concreta que sea. Sino si estas herramientas proporcionadas por cada uno de los framework son suficiente para lograr los objetivos de negocio autoimpuestos.

Así pues, ¿es suficiente una anotación @Autowire para decidir que implementación utilizar? ¿necesitamos algo más? ¿Cómo vamos a resolver todo esto dentro de nuestra arquitectura de software?

Cada caso requiere, probablemente, sus propias respuestas, así que me voy a limitar a daros unas pinceladas de que se puede hacer para resolver estos problemas.

Desde el punto de vista de backend

La verdad es que depender solo de anotaciones estándar no es suficiente. Aunque estos mecanismos de autowiring parecen magia se limitan a seguir una serie de reglas preestablecidas. Así pues, usarlos adecuadamente para satisfacer los objetivos de negocio, requiere considerar:

  • Aislar distintas implementaciones en diferentes módulos.
  • Depender en abstracciones y no en implementaciones.
  • La dependencia de la implementación debería incluirse en los scripts de construcción de la aplicación.
  • Si aplicamos principios de desarrollo de software como herencia, el mecanismo de descubrimiento de la implementación debería descansar en ficheros de propiedades, registros en la Base de datos o cualquier otro mecanismo no basado en código (que fija una dependencia fuerte)
  • No deberíamos olvidar sobre las herramientas de extensibilidad del propio lenguaje, por ejemplo la capa de extensiones de servicio en el lenguaje Java.

Antiguos sistemas de frontend

Hablar de "antiguos" sistemas de front es hablar de páginas PHP, JSP o incluso plantillas ERB, en todas ellas no tenemos un framework JavaScript moderno "dando vueltas", sino que la vista se pinta desde el "servidor". No obstante ser "antiguo" no significa que estos problemas no se hayan planteado ni se hayan resuelto.

Así, algunos frameworks como RoR proporcionan un mecanismo de extensibilidad sobresaliente, productos como Drupal o WordPress tienen la extensibilidad en mente, mientras que en otros casos se debe incluir el mecanismo de una forma más manual. En cualquier caso, deberían cubrirse los siguientes aspectos:

  • La vista debería estar aislada de la lógica de negocio.
  • La vista debería estar separada en fragmentos pequeños, componentes.
  • La vista debería poder reemplazada utilizando mecanismos de extensibilidad, RoR utiliza su propio mecanismo, en otros frameworks para hacer la misma magia hay que utilizar aproximaciones específicas, por ejemplo utilizando JSP nos veríamos obligados a utilizar un RequestDispatcher personalizado.
  • Las vistas no son sólo plantillas de un HTML sino que también incluyen texto. Como bien sabéis el texto no debe embeberse directamente en vuestra vista sino que debería depender de ficheros de mensajes externos. Lógicamente, estos mensajes podrían personalizarse en función del cliente, incluso podría darse el caso de que puedan almacenarse en una BBDD para ser fácilmente modificables. Si trabajáis con Java, aspectos a considerar incluyen la gestión de ResourceBundle personalizados.
  • Un problema similar ocurre con los recursos CSS. Estos recursos también dependerán del cliente. Es un problema de elegir el tema adecuado, como se gestionan las dependencias entre lo personalizado y lo general, etc.

Nuevos sistemas de frontend

Los problemas no desparecen cuando utilizamos una nueva tecnología, de hecho en algunas ocasiones se hacen más intensos. Desarrollar un framework con una nueva tecnología como React o Angular implica que los problemas los debemos resolver de una forma distinta, a decir verdad más parecida a cómo se hace en backend que a los viejos sistemas de frontend.

Por centrarnos, en los sistemas de frontend "antiguos" la responsabilidad de que vista cargar dependía del "framework global", que podía elegir, entre varias, la más adecuada para ejecutar una determinada tarea. Hoy en día, la responsabilidad recae, en el frontend, convertido así en el único responsable de proporcionar los mecanismos de extensibilidad adecuados (esto es matizable, pero dejémoslo estar así).

Qué debemos hacer

  • Separar adecuadamente las funcionalidades, de tal forma que podemos decidir que módulos se cargan y cuales no de forma independiente.
  • Aislar entre funcionalidades comunes y detalles o implementaciones concretas.
  • Utilizar mecanismos proporcionados por el propio framework (contextos en REACT, IoC en Angular) que nos van a permitir decidir que componente cargar.
  • Aprender del camino andado en los frontend "antiguos", en particular en aspectos tales como ficheros de mensajes, temas CSS… No reinventar la rueda.