La verdad es que llevo algunos dias pensando en si escribir o no este post, y, por fin, me he decidido a hacerlo. Tal y como se puede sobreentender por el titulo del post, la idea es hablar sobre el uso de elementos como IDbSet ( o sus correspondientes en versiones anteriores ) en el caso de EF como repositorios o bien hacer alguna abstracción sobre este patrón. La idea, surge después de leer algún que otro post sobre este tema y también algunos comentarios que me llegan de twitter ( de personas que, por cierto, bien podían preguntar directamente, pero esto es otro tema ..). Lógicamente, como todo lo que digo y cuento en mi blog esta es una opinión personal y yo no tengo/creo tener, al contrario que otros, una patente de corso por la cual todo lo que haga o diga sea correcto.
Entremos en materia. Yo no voy a negar el hecho de que IDbSet es realmente un repositorio, de hecho, una implementación buenísima gracias a todo el trabajo que podemos hacer con árboles de expresiones, puesto que este elemento, cumple todos los criterios que marca este patrón tal y como lo describe Fowler aquí. Por lo tanto, dicho esto, tranquilamente podríamos hacer uso de los diferentes elementos expuestos por nuestras unidades de trabajo generadas por EF o hechas por nosotros si usamos Code First en aquellos puntos donde fuese necesario. Hacer esto, desde luego es una simplificación, puesto que cierto código que podriamos escribir ya lo tenemos de forma gratuíta y por lo tanto, la pregunta es porque hacer una abstracción sobre este concepto cuando en realidad ya lo tienes implementado ( lo mismo se podría aplicar a otros ORM). La respuesta a esta pregunta no es fácil, de entrada, yo diría que nunca, excepto cuando tengas algún escenario dónde esta abstracción te pueda suponer una serie nuevas de ventajas que sean interesannes para tu solución. A continuación, intentaré enumerar algunos elementos que podrían hacer inclinar la balanza en el hecho de usar o no una abstracción para representar un repositorio.
- Las atribuciones de cada agregado: Cuando se definen repositorios, y por lo tanto, los agregados de un dominio, también se definen las atribuciones que cada uno de estos elementos va a tener. De tal forma, que, no todos los agregados tienen porque tener las atribuciones de los demás. Es decir, pueden existir agregados dónde no puedas realizar ciertas operaciones de mantenimiento de datos o no puedas realizar determinadas consultas. El uso de un elemento como IDbSet<T> no facilita esta tarea para nada, puesto que el consumidor no tendrá una forma de ver una “restricción” de las operaciones que este agregado puede tener, ni siquiera, de hecho, puedes impedir que una operación ( que no forma parte de sus atribuciones ) pueda ser realizada. Lógicamente, el hecho de poner una capa de abstracción sobre la definición de los repositorios de cada agregado es una forma de poder dar y dejar “en un contrato” cuales son las atribuciones que cada agregado tiene.
- Los repositorios de los agredos no tienen porque ser solamente genéricos, es decir, estos repositorios pueden tener operaciones de consulta importantes dentro del dominio y querer que estas se muestren de forma explícita a los consumidores. De esta forma permitimos que el consumidor del repositorio se pueda desacoplar del criterio de consulta que una operacion del repositorio pueda tener, permitiendo un mejor mantenimiento en caso de cambio de este criterio de consulta. Por supuesto, este problema se podría minimizar haciendo uso del patron specifications pero también la abstracción podría dar un soporte mejor para esto.
- Cuando uno define un agragado raiz y define las entidades que forman parte de este agregado en realidad también se está decidiendo como será la carga de ciertas asociaciones entre entidades. En ocasiones, queremos/necesitamos imponer que una raiz se carge siempre con ciertos elementos del agregado, por ejemplo, simplemente de caracter didáctico, siempre que carguemos un Order queremos incluir sus detalles. Hacer esto implica establecer de forma explítica un Include en todos los sitios de nuestros código dónde manejemos este agregado raiz. Puesto que el negocio cambia y el dominio por lo tanto también, esto, tiene ciertas implicaciones de mantenimiento, puesto que no es algo localizable de forma directa. El hecho de poner una abstracción nos permite tener un solo punto dónde estos criterios de carga se esetablezca y que por lo tanto, el mantenimiento sea mucho más sencillo y adaptable.
- Incluir conceptos para la monitorización o la salud es mucho más sencillo cuando puedes escribir código en tus abstracciones, antes, durante o después de las operaciones del repositorio.
- IDbSet es más, es decir, esta interfaz posee más conocimiento de lo que realmente nosotros necesitamos cuando hacemos uso de los repositorios. De hecho, tiene conocimiento por ejemplo sobre el tracking de los objetos, algo que no es su responsabilidad ( sino de la unidad de trabajo ) y por lo tanto nos enseña realmente más de lo que necesitamos sabes y por lo tantoes una vía abierta para introducir otros problemas.
Seguramente que me dejo algo por cada lado, aún así, yo creo que más o menos ya nos podemos dar una idea de lo tenemos con una o con otra opción. Lógicamente, debemos ser cada uno de nosotros los que elijamos intentando entender las dos opciones y cual va a ser para nuestro desarrollo la mejor, es nuestra REPONSABILIDAD y por lo tanto hay que tomar la decisión valorando bien pros/contras.
P.D; Para el siguiente post intentaré hablar sobre otro tema del que se habla y es si poner un contrato base para nuestras unidades de trabajo o no…
Saludos
Unai