Best Practices

Artículo disponible en epub y pdf:

Cómo usar repositorios de datosCómo usar repositorios de datos epub

 

 

Me llama mucho la atención cómo a lo largo de los años puedo haber trabajado en aplicaciones con .NET en C#, con javascript utilizando Angular y NodeJS, y también con PHP cuando estaba más centrado a hacer módulos en Drupal, y cómo con todos esos lenguajes con sus respectivos entornos de desarrollo se pueden aplicar las mismas buenas prácticas.

Hay veces que éstas se refieren a procedimientos de trabajo o al modo de estructurar un proyecto, otras hacen referencia a aplicar patrones de diseño e incluso antipatrones para detectar precisamente lo que no se hace bien. En otras ocasiones, son sencillamente prácticas habituales que hace la mayoría de la gente que usa una tecnología en particular (de ahí, por ejemplo, los proyectos de scaffolding).

Un buen proyecto software debe oler a buenas prácticas, de principio a fin, desde la arquitectura del mismo hasta la evolución de las funcionalidades que se le van añadiendo poco a poco.

Nada peor que una aplicación, proyecto o sistema en el que no se pueda ver claramente cómo están implementadas esas buenas prácticas.

Y es que además tienen algo en común: la mayoría de ellas son fáciles de implementar.

Y como me gusta escribir... pues voy a ir hablando de ellas, de aquellas con las que yo directamente he visto que tienen más impacto en la mejora de la calidad de un proyecto software.

Un aviso a navegantes: en muchas ocasiones los beneficios de estas recomendaciones no se ven claramente, ni siquiera en el corto plazo, y su impacto positivo a medio y largo plazo es más una cuestión sutil que sólo se aprende cuando has cometido todos los errores posibles y entonces se hace la luz y dices algo como, "ahora sí que lo entiendo".

Para mí la implementación de repositorios de datos es una de las prácticas más útiles en cualquier proyecto software.

El principio general de implementar un repositorio de datos vendría a ser el siguiente:

Todos los accesos a los datos que utiliza una aplicación (vengan de bases de datos, entidades de datos del mundo cloud, datos en caché, etc.) deben realizarse desde un único punto.

Dependiendo de la tecnología y del tipo de aplicación que se esté desarrollando, ese único punto se entiende por un módulo con sus clases, una librería, una API, un servicio REST, etc. Lo importante es que todos esos accesos estén centralizados y bien localizados en el proyecto.

¿Qué beneficios se obtiene de esto? Son muchos, muchísimos, algunos de ellos no fáciles de ver en un principio.

Por resumir, centralizando el acceso a los datos conseguimos:

  • Cumplimos con el principio SoC, (separation of concerns separación se intereses), básico en el desarrollo ágil. De este modo tenemos una parte muy concreta y localizada del proyecto especializada en acceder a los datos que utiliza.
  • Se evitan duplicar código repitiendo los mismos accesos a los mismos datos a lo largo de la solución. En lugar de ejecutar algo como un "select * from dbo.users where userId = 20" cada vez que se necesita la información de un usuario, tenemos algo mucho más general como "data.getUserById(20)" (esto es un sencillo ejemplo en pseudocódigo, eh!).
  • Se simplifica la solución y se reduce el número de líneas de código. Al estar encapsulado el acceso a los datos en métodos muy concretos, no necesitamos ensuciar u ofuscar la lógica de negocio de la aplicación con detalles de acceso a los datos (tales como abrir una conexión a la base de datos, cerrarla, etc.), y ni hablar cuando esos accesos con más complejos y necesitamos transacciones o joins anidados.
  • Es más fácil ejecutar un análisis sencillo de profiling: tan sólo repasando en el objeto repositorio a qué datos se accede y cómo, podemos determinar qué índices hacen falta, en el caso de bases de datos relacionales e implementar tests de rendimiento que se puedan ejecutar continuamente.
  • Conseguimos desacoplar la solución de cómo ésta accede a los datos. ¿Y si mañana cambiamos de motor de base de datos? ¿Y si por escalabilidad se decide distribuir los datos en diversas bases de datos incluso con diversas tecnologías? Nada peor que empañar todas las partes de la solución con los detalles de acceso a los datos. Puesto que todo está encapsulado en el repositorio, tan sólo hay que tocar éste. El resto de la aplicación, ni se entera del cambio.
  • Con la implementación del repositorio, al estar éste desacoplado, se pueden crear tests específicos, unitarios y de rendimiento.
  • Es trivial también crear mocks. ¿Qué pasará cuando la tabla "usuarios" tenga cien mil registros? ¿Aguantará el front-end? Con un repositorio de datos es fácil simular diversas situaciones en previsión de problemas de escalabilidad pero sobre todo, que esas situaciones pueden estar respaldadas por pruebas.
  • Al estar todos los accesos centralizados, es más fácil para la persona encargada del desarrollo de la base de datos y su acceso trabajar en esa área especializada.
  • Al no estar dispersos por la solución todos los accesos al repositorio de datos, las migraciones del mismo no terminan en pesadilla...

Hace un tiempo se hablaba en ciertas tecnologías de la capa DAL (data access layer), que vendría a ser el mismo concepto pero más ligado a bases de datos relacionales.

Si se utiliza algún tipo de ORM que crea objetos alrededor de las entidades de datos, ¿deberían esos objetos viajar a lo largo de la lógica de negocio? Esta pregunta me la encuentro recurrentemente, y mi opinión es que no, puesto que de ese modo la aplicacíon tiene una dependencia clara de la tecnología ORM utilizada. Los objetos que se deberían distribuir a lo largo de la lógica de negocio serán similares pero no ligados a la tecnología ORM usada (son los que se llaman los data transfers objects o DTOs).

A ver, al final hay que aplicar el sentido común (con un poco de experiencia y hasta de suerte...): si trabajamos en un proyecto cuyo ciclo de vida es muy claro y sabemos que se terminará y que no va a evolucionar, pues nos podemos tomar cierto tipo de licencias, pero si tenemos entre manos un producto o proyecto que crecerá y evolucionará en funcionalidad a lo largo de muchos años, entonces sí hay que cuidar mucho este tipo de decisiones y dependencias, así como aplicar con mayor rigor aún todas las buenas prácticas posibles.

Sí, ya sé, es que a veces no hay tiempo, el proyecto viene muy cerrado en horas, etc. De acuerdo, razón de más para aplicar este tipo de buenas prácticas desde el principio, porque sólo así se podrá avanzar en el proyecto con productividad y mayor rapidez.

Todo esto es más fácil de implementar que de describir; sin embargo, un buen proyecto software que necesita de cualquier mecanismo para persistir datos, tiene que acceder a ellos a través de un objeto / módulo / librería que implemente esta buena práctica.

Algunos enlaces de interés:

The Repository Pattern

Repository Pattern Done Right

Desde hace unos años participo en cierta medida en asuntos comerciales además de mi dedicación casi completa a dirigir un equipo de desarrollo, involucrándome, cómo no, en todo tipo de tareas técnicas, decisiones de diseño y arquitectura, toma de feedback de clientes, etc.

En este tiempo me ha llamado mucho la atención cómo bastantes clientes pontenciales de los productos que comercializamos se interesan por conocer el motor de base de datos que usa nuestro software. Los hay quienes le dan todas las bendiciones a SQL Server, otros que no cambiarían Oracle por SQL Server y unos pocos que nos han preguntado si el sistema podría funcionar con Cassandra (...). Nada que objetar, salvo su percepción completamente errónea de que el producto va a ser rápido y eficiente o lento y poco escalable según la base de datos que use, nada más.

Esta suposición, entendible para cierto perfil no técnico, la he visto también en otro tipo de perfiles muy técnicos y entendidos en software, para mi asombro.

La escalabilidad de un producto software no depende necesariamente de la base de datos que use, ni mucho menos: ésta es un elemento más de la larga lista de características que se le deben exigir a un producto software escalable.

No sólo he tenido que sufrir este tipo de polémicas en el eterno debate que si SQL Server  / Oracle, sino que además, todo lo que huela ahora mismo a volúmenes grandes de información rápidamente se asocia a la necesidad de una solución big data con Hadoop o MongoDB, confundiendo necesidades de almacenamiento con necesidades de análisis y procesamiento de la información.

Pero, ¿qué entendemos por escalabilidad? Esto va a depender el tipo de productos del que estemos hablando: para un portal web es sin duda el soportar un número alto de usuarios simultáneos, para una aplicación de análisis de datos, el poder captar y procesar cierto volumen de información; para un sistema de gestión de dispositivos físicos, como contadores digitales, por citar algo más ligado a mi actividad, no es lo mismo gestionar mil que cien mil dispostivos en un mismo sistema, con cinco o con cien operadores simultáneos.

Si un producto software usa una base de datos, de cualquier tipo que sea esta (relacional, no-sql, documental, etc), ésta por sí misma no va determinar el rendimiento del producto.

Esto que parece una perogrullada lo he tenido que explicar más de una vez a personas que se dedican a desarrollar software...

¿Qué determina un buen uso de una base de datos?

Este es un tema extraordinariamente amplio, pero para dar una idea de que el buen uso de una base de datos no es del todo trivial, podemos decir que no puede haber deficiencias en el diseño, sin redundancias innecesarias, las consultas a la base de datos deben ser sencillas y eficientes, se debe minimizar el número de consultas, cada consulta debe traerse la información estrictamente necesaria, las transacciones se deben dejar para las operaciones imprescindibles, debe hacerse un estudio concienzudo de los índices necesarios según la naturaleza de las consultas a realizar, según los casos habría que optar por las estructuras eficientes que ofreza el mismo gestor de bases de datos (particionado horizontal y vertical, por ejemplo), no mezclar una base de datos pensada para almacenamiento histórico con la base de datos de trabajo, dejar, si se puede según la idiosincrasia del producto, los procesamientos masivos de información para momentos que no interfieran en las ventanas de operación de los usurios clientes, y un largo etcétera .

Si este tipo de cosas no están bien resueltas, de nada nos servirá el mejor motor de base de datos.

En mi experiencia he visto claramente cómo la forma de almacenar la información determina lo sencillo o complejo que puede ser gestionarla por el software cliente de alto nivel. En ocasiones no falla cómo accedemos a la información, sino cómo esta información se encuentra almacenada en el repositorio de datos.

La forma en que necesitamos la información y cómo ésta se encuentra almacenada van de la mano.

Podemos incidir y hacer un buen trabajo a ese nivel mejorando el modo con el que nuestra aplicación accede a la base de datos, pero me temo que no queda la cosa resuelta del todo.

¿Qué ocurre con la arquitectura del propio sistema?

En cierto sentido, esta arquitectura va emergiendo a medida que intentamos que admita más y más dispositivos, usuarios o las entidades que representen la escalabilidad para nuestro producto.

Sin embargo, cuando hablamos de sistemas con grandes volumenes de procesamiento de información, tareas, procesos, etc. la arquitectura del sistema software es fundamental. Aquí ya hablamos de desarrollar software a otro nivel, con otro tipo de estrategias de diseño y desacoplamiento entre subsistemas para que cada uno haga su tarea de manera muy eficiente.

No hay estrategias generales que resuelvan cada caso concreto, pero sí buenas prácticas arquitecturales.

El poder distribuir estos subsistemas entre servidores distintos es todo un reto también de diseño para que todas las instancias puedan realizar su trabajo concurrentemente balanceando el trabajo, tanto si nos apoyamos en terceros sistemas para este propósito como si no.

Una aplicación que funciona bien para diez usuarios concurrentes no tiene nada que ver en diseño, arquitectura y diseño de repositorios de datos para la misma aplicación que tenga que dar servicio a 10k usuarios, por poner un ejemplo. La arquitectura no tendrá nada que ver, el diseño general y los microdiseños serán muy diferentes de un sistema a otro y el diseño y uso de la base de datos también serán extraordinariamente distintos.

La cuestión es que pocas veces comenzamos a desarrollar un nuevo producto sabiendo a ciencia cierta lo escalable que tiene que llegar a ser y en qué momento de su tiempo de vida deberá admitir más usuarios, dispositivos, etc. Hace falta mucha experiencia para prever esta arquitectura y los diseños adecuados. ¿Y entonces?

No existen soluciones mágicas para casi nada, pero lo que sí puedo asegurar es que si se comienza haciendo diseños limpios, con mucho esfuerzo en generar código desacoplado y de calidad, con una batería de pruebas suficientemente amplia, exhaustiva y mantenible, si nos esforzamos en cada fase del desarrollo del producto en identificar qué partes presentan cuellos de botella mediante análisis de rendimiento, etc. podremos tener un sistema para el que poder escalarlo no se convierta en algo dramático.

En ciertos sistemas complejos y más o menos grandes, todo, absolutamente todo cuenta: una iteración simple entre los elementos de una lista parece algo inocuo, pero cuando se ejecuta en un servidor cientos de miles de veces en un día, puede presentar un problema de rendimiento cuyo efecto se acumula desastrosamente a otros, presentando finalmente un problema global de rendimiento. Los detalles sí que importan.

Escribir código que funcione es nuestro trabajo, también es escribirlo de manera que funcione y que sea lo más limpio y simple posible, pero también que esa eficiente, lo que ya no es tan trivial en algunas ocasiones.

De hecho, existen libros dedicados a este asunto, como uno al que recurro habitualmente: Pro .NET Performance, por poner un ejemplo.

Otro recurso que uso confrecuencia (ligado a las tecnologías que más uso), es Improving :NET Application Performance And Scalability.

Por último, nada peor que encontrar problemas de rendimiento cuando el producto ya está en producción, si eso ocurre es que no se ha hecho un trabajo del todo bueno probando el sistema con anterioridad. No sólo hay que realizar tests unitarios, de validación e integración, también de rendimiento.

Así las cosas, cada vez que un cliente potencial con el que seguramente tenga media hora sólo para hablar, me pregunta qué base de datos usa el sistema, comprenderéis lo complicado que a veces resulta defender una respuesta cuando el cliente está más predispuesto hacia otro tipo de gestor de bases de datos que la que usamos.

¿Por qué leer El Libro Negro del Programador?

Adquirir desde:
Amazon (kindle eBook / papel)
CreateSpace (papel)
PayHip (epub / mobi / pdf)

El libro negro del programador.com
Segunda Edición - 2017

Archivo

Trabajo en...

Mis novelas...