De abstracciones y repositorios

No esperaba yo verme en esta situación de tener que defender el uso de abstracciones, después de haber criticado los arquitectos y sus superarquitecturas, las capas que no aportan ningún valor o el uso de abstracciones en cosas que no pueden abstraerse, pero igual que con los comentarios en el código, creo que el tema merece una reflexión un poco más profunda.

Ya lo he dicho otras veces, el desarrollo de software no está exento de modas y, como casi todas las modas, éstas vienen en ciclos. Muchas veces, después de una fase de hype a favor de una tecnología o técnica sigue una fase de completo desprecio por ella, pasando en poco tiempo de un extremo a otro. Esto hace que en ocasiones nos dejemos llevar por razonamientos simplistas, recordando sólo el abuso que se ha hecho de algo y olvidando las partes positivas que podía aportarnos.

El valor de las abstracciones

Recientemente me he encontrado en la enésima discusión sobre repositorios sí o repositorios no, y he leído algún artículo en el que se critica duramente las arquitecturas basadas en capas, cosa que, por cierto, yo también he hecho en alguna ocasión.

En ambos casos la discusión subyacente trata sobre el uso y abuso de abstracciones, lo que sin duda es un tema interesante y que debe ser considerado con cuidado, pero en mi opinión el principal argumento que se da para no usar abstracciones es erróneo.

Este argumento se centra en que las abstracciones protegen frente a los cambios y, por tanto, si no vas a necesitar cambiar esa parte del sistema, no es necesario introducir una abstracción. El ejempo más típico de esto es si no vas a cambiar de nunca de ORM, para qué vas a abstraerlo detrás de repositorios.

Es cierto que introducir algún tipo de abstracción sirve para aislar distintos componentes de un sistema, introduciendo un mecanismos que nos permita evolucionarlos por separado y evitando que los cambios en uno afecten al otro, pero una abstracción es más que eso.

El proceso de abstraer consiste en aislar la esencia de algo de manera que podamos tratarlo sin tener que considerar aquellas características que son accidentales y por ello innecesarias para definir ese algo.

Podemos aplicar este proceso a muchos niveles, y es algo que hacemos continuamente. Cuando introducimos una variable para almacenar un resultado temporal y poder darle un nombre, estamos abstrayendo la forma de calcular ese resultado temporal. Cuando extraemos parte de código a un método, estamos haciendo lo mismo, estamos separando una parte de un proceso y utilizando un nombre que nos permita referirnos a ella, sin necesidad de conocer cómo está implementada.

Estas abstracciones, muy relacionadas con el principio DRY, no sólo nos protegen frente a los cambios, sino que además aportan algo más importante: ayudan a elevar el nivel del discurso, permitiéndonos razonar en términos más generales y facilitando la comprensión del sistema.

El valor real de muchas abstracciones es ayudarnos a manejar sistemas complejos encapsulando ciertas áreas, y no tanto protegernos de cambios en esas áreas (aunque esto no deja de ser un agradable valor añadido).

Abstracciones sobre el ORM

Por ver un ejemplo, y volviendo al (recurrente) tema de los repositorios (del cual podéis ver mi opinión en estos posts sobre repositorios), utilizar repositorios para encapsular el acceso a datos no debería tener como finalidad principal protegernos frente a cambios en el ORM que estamos utilizando, sino proporcionarnos un lenguaje de más alto nivel para razonar sobre un problema.

Efectivamente, escribir una consulta directamente contra un DbContext o un ISession es mucho más potente y flexible que encapsularla en un repositorio y nos permitirá utilizar toda la potencia de nuestro ORM, pero abstraer esa consulta en un repositorio (o con cualquier otra técnica, por ejemplo un Query Object) nos posibilitará darle un nombre y nos ayudará a entender mejor las partes del código donde se usa esa consulta.

Podemos ver estas abstracciones como una fachada que oculta la complejidad de manejar el ORM. Es más sencillo y legible utilizar un repositorio con una consulta para obtener clientes preferentes que han comprado algo en el último mes que generar inline esa consulta con LINQ.

Además, para los amantes de los principios SOLID y todas esas cosas, el uso de repositorios (y especialmmente el de Query Objects) hace que limitemos la superficie de las dependencias en lo que podríamos considerar una aplicación del principio de segregación de interfaces.

Con todo esto no pretendo decir que lo mejor sea siempre encapsular el ORM detrás de repositorios o Query Objects, la respuesta, como diría un gallego, es que depende, pero lo que sí hay que tener en cuenta es que el motivo de base para decidir si encapsular o no el ORM no debe ser si lo vamos a cambiar en un futuro o no (seamos realistas, probablemente no).

Conclusiones

Normalmente, cada periodo de popularidad es seguido por una reacción contraria hacia lo que fue popular en su momento, y cuando mayor ha sido la popularidad, mayor será la reacción en contra. Es complicado abstraerse (valga la redundancia) de las corrientes de pensamiento de cada momento, pero es necesario intentar mantener cierta distancia y ver con perspectiva qué nos aporta cada técnica.

Que se haya abusado de abstracciones en arquitecturas ultracomplejas no quiere decir que las abstracciones no aporten ningún valor, y ese valor va más allá de la mera protección ante el cambio.

Desarrollar software es una actividad muy compleja; gracias a las abstracciones podemos obviar determinados detalles en ciertos momentos para que seamos capaces de gestionar esa complejidad: las abstracciones nos permiten quitar los árboles para poder ver el bosque.

3 comentarios en “De abstracciones y repositorios

  1. Hola Juanma, este artículo se me había escapado.

    Me ha venido muy bien leerlo, hace poco en un nuevo proyecto que he empezado con Javier Campos, yo apostaba por usar repositorios por usar una forma inflexible de hacer las cosas para evitar la ambigüedad a la hora de decidir donde implementar algo. Él por contra apostaba por disponer del DbSet en el negocio para aportar flexibilidad y aprovechar las bonanzas del EF como ORM. Los dos estábamos de acuerdo en que no cambiaríamos el ORM :), y al final hemos optado por una solución intermedia. Asumimos EF como ORM, disponemos del DbSet en el Negocio pero aislamos el trabajo «sucio» con EF en métodos extensores de DbSet, clases base de negocio en métodos protegidos o métodos propios privados.

    En el ejemplo de Clientes Preferentes, tendríamos:

    public static IEnumerable PreferentialCustomers(this DbSet dbset) { ... }

    Todavía no lo hemos terminado de concretar, pero creo que podría resultar bien.

    Saludos!!

  2. Hola,

    Es una buena salida. Es algo parecido a lo que contaba en este post https://blog.koalite.com/2011/12/tipos-de-repositorio-el-no-repositorio/, pero cambiando los QueryObjects por Extension Methods.

    Le veo la ventaja de la flexibilidad y la posibilidad de encapsular consultas complejas para «ponerles un nombre de negocio». Como pegas, que dejas el DbSet abierto a usos «poco recomendables» y pierdes «discoverability» al no tener todas las consultas agrupadas en un único sitio.

    Al final depende mucho de los gustos de cada uno, pero yo creo que puede funcionar bien.

    Un saludo,

    Juanma

  3. Justo vengo de hacer una pequeña reunión de seguimiento con Javi y me ha enseñado una especie de QueryObject que se ha currado hace poco muy chulo, con fluently y me ha gustado mucho.

    Tendré en cuenta tus palabras.

    Saludos!

Comentarios cerrados.