En este último tiempo, el patrón repository se está escuchando muy seguido en el mundo del desarrollo web y no es para menos, ya que le da una gran flexibilidad, mantenimiento y calidad a nuestro código.
En este artículo vamos a ver cómo podemos utilizarlo para que también brindé optimización a nuestro sistema.
El patrón repository
En otros artículos de Laravel Tip ya he comentado que el patrón de repositorios nos permite ser independientes del ORM (Eloquent en Laravel) o de la base de datos (MySQL, Postgresql, etc.).
Pero me quiero detener en el tema de Eloquent y el patrón repository. Algunas personas dicen que el patrón repository no es útil o es innecesario en Laravel, pero eso no es del todo cierto. Veamos cómo lo podemos utilizar para sacarle el jugo a este patrón.
El problema de los Modelos Eloquent
Los modelos de Eloquent nos promocionan TODOS los métodos necesarios para interactuar con la base de datos… pero… esperen un segundo… ¿es realmente necesario tener todos los métodos disponibles en todo momento?
La respuesta rápida sería un «si» rotundo, porque realmente es muy cómodo tener los métodos find
, get
, update
, save
, etc. a nuestra completa disposición. Pero supongamos que necesitamos hacer una exportación de ciertos datos a un archivo Excel, en este caso, ¿para que nos servirían los métodos de actualización e inserción?
Por ejemplo, para hacer una exportación (digamos, de productos), haríamos algo así:
Eloquent nos devolverá una colección, donde cada uno de sus elementos es un objeto del modelo Product
. Y esto significa que tenemos acceso a los métodos update()
y save()
(entre muchos más) que realmente no los vamos a utilizar en una exportación. Por lo tanto, es costoso para nuestra RAM instanciar un modelo Eloquent, si solo vamos a realizar una operación de lectura (o escritura) únicamente.
Violación de Principio SOLID
Tener un objeto que nos brinde demasiados métodos que no vamos a necesitar, como son los objectos de Eloquent, estamos violando el principio SOLID de Segregación de la Interfaz.
Este principio justamente nos dice que no debemos tener objetos con métodos que no vamos a utilizar.
Para saber más del Principio de Segregación de la Interfaz y todos los principios SOLID, te dejo un guía: https://www.laraveltip.com/guia-definitiva-de-principios-solid-explicados-con-laravel/
Utilizando el Patrón Repositorio
Como vimos, Eloquent nos brinda modelos para realizar tanto operaciones de escritura, como operaciones de lectura, y esto no es aconsejable cuando se busca optimización. Y aquí es donde entre en juego el patrón repositorio.
Lo aconsejable es tener una clase para las operaciones de lectura y otra clase para las de escritura. Y aquí es donde podemos aplicar los repositorios.
Refactorizando el ejemplo de la exportación de productos
Como los objetos de Eloquent nos permite hacer todo tipo de operaciones contra la base de datos, vamos a utilizar los repositorios para dividir las funcionalidades.
El ejemplo anterior lo podríamos reemplazar por:
Como vemos, escribimos un poco más de código pero, en compensación, ganamos legibilidad ya que el objecto repository tiene un método mucho más expresivo de lo que se esta haciendo y para que.
También vemos que en el constructor estamos inyectando una interfaz llamada ProductReportRepository
, es una interfaz muy sencilla:
Y veamos como quedaría la clase concreta que es la encargada de hacer la consulta:
Y por ultimo, debemos «bindear» la interface con la clase concreta para que el controlador sepa que clase se esta utilizando, realmente. Así que, en el método register()
del archivo AppServiceProvider.php
, ponemos el siguiente código:
$this->app->bind(ProductReportRepository::class, ProductReportSqlRepository::class);
Y eso es todo. Ahora tenemos una clase que se dedica a obtener todos los registros de la base de datos.
Pro vs. Contras
Pros:
- No instanciamos grandes objetos con operaciones que no vamos a necesitar.
- La consulta es realmente rápida, ya que estamos utilizando SQL puro.
- Cumplimos el principio SOLID de Responsabilidad Única.
- Cumplimos el principio SOLID de Inversión de dependencias, ya que si en el futuro decidimos que esos datos no estén en una base de datos relacional, simplemente creamos un nuevo repositorio que implemente la interfaz y escribimos el código necesario para esta nueva implementación.
- Y todos los demás beneficios del patrón repository: flexibilidad, bajo acoplamiento y facilidad de testear.
Contras:
- Lleva un poco más de trabajo ya que escribimos más código y necesitamos crear clases adicionales: la interfaz del repositorio y la implementación del repositorio. Y además, registrarlas en el ServiceProvider.
Conclusión
No digo que debemos reemplazar automáticamente los modelos Eloquent por este tipo de solución. Es más, podríamos tener un repositorio que implemente un modelo Eloquent. Hay casos que necesariamente vamos a necesitar obtener datos y actualizarlos, y ahí Eloquent nos va a servir. Pero sin dudas, en proyectos importantes, debemos separar las operaciones de escritura de las operaciones de lectura.
debemos separar las operaciones de escritura de las operaciones de lectura? Esto no me queda claro podría darme un ejemplo o explicación de como conseguir esto.
Como va Enrique? Una operación de lectura le diga a una consulta a la base de datos que solo trae datos (un SELECT) y tu programa solo maneja esos datos para mostrarlos al usuario (o, como en el ejemplo del artículo, para exportarlos a un Excel). Y una operación de escritura es aquella que inserta, elimina o actualiza datos de la base de datos, por ejemplo una registración de un nuevo usuario.
Cualquier duda, déjala por aquí. Saludos.
hola matias, entonces tendríamos que crear un repositorio para cada operación de es escritura?
Como estas Jipson? Si, si vas a usar esta arquitectura, si.
Que tal, de mi parte he usado este patron, pero donde lo aprendí, vi que se creaba un repo por cada model, esta bien asi? Adicional a ello tenia un BaseRepo, de cual todos los repos extendian de este, en BaseRepo tenia las funciones como create, update, delete, etc.
Esta bien que use un repo por cada model o tendria que cambiar algo?
Hola Marco, como estas?
Utilizar el patrón de esa forma te da el beneficio de poder testear y desacoplar la capa de persistencia. Si eso es lo que buscabas y tu proyecto lo requiere, esta perfecto.
Ahora, si tu proyecto no va a cambiar la base de datos en el futuro, ni tampoco estas haciendo test automatizados sobre esto, entonces no los necesitas.
en java tenemos los entity(modelo), reposity(métodos comunes), service(lógica de trabajo), controller (endpoint).
¿En laravel hacen la lógica en que seccion?
Hola Omar, como estas?
Donde poner la lógica de negocio en Laravel es una gran duda ya que Laravel no te dice donde debes hacerlo.
Muchos dicen que el mejor lugar es en los Modelos porque son los que están más cerca de los datos. Pero las clases de los Modelos terminan siendo enormes y muy poco reutilizables.
Para mí, lo mejor es meter toda la lógica en clases Services.
Gracias por comentar.
Hace poco empece a ver Laravel.
quería implementar un patrón repository, con una capa DTO.
En java dentro de los DTO podemos hacer la validación de tipos con etiquetados.
Pero aquí no se si usar el Validate Form para hacer un objeto DTO. Y muchas otras cosas que me plantean dudas.
Creo que me volveré ha Java.
Gracias por tu respuesta.
Como estas Omar?
En Laravel lo mas importante son sus Routes, Controllers, Models y Views, luego el resto de clases (FormRequest, Scopes, etc.) son solo «herramientas» que el framework te brinda, pero el framework es bastante flexible para que utilices la arquitectura que quieras.
Si quieres utilizar Laravel como venias acostumbra en Java, esta perfecto. Después depende de uno y del proyecto que estes haciendo, si quieres utilizar el resto de cosas que tiene el framework o no.
Acá explico unos principios de otra arquitectura con Laravel, por si la quieres ver: https://www.laraveltip.com/desacoplando-laravel-de-tu-aplicacion/
Saludos.
Esta interesante, aunque me gustaría un ejemplo mas robusto.
Igual excelente todo su contenido.
Muy bien explicado, muchas gracias! Como dices la mayor ventaja es poder desacoplar la base de datos a la hora de hacer tests.
Pero no sé, en mi opinión (https://youtu.be/uZp2HPbibA8) tenemos que dejarlo de lado y hacer test con la base de datos que laravel lo pone muy sencillo.
Buen video Victor! Una aclaración, yo no soy partidario de usar una forma u otra. Como siempre, todo depende del caso.
En un sistema chico/mediano, a full con Eloquent. Pero cuando empieza a crecer el sistema y aparecen nuevos problemas, tal vez es mejor empezar a pensar en refactorizar a repositorios.
Gracias, siempre es grato leer este tipo de contenido un poco mas avanzado que nadie habla en los cursos web.
Muchas gracias Eduardo!
A mi opinión cada vez más les da por DIVIDIR TODO, la tendencia ha ido en DIVIDIR DIVIDIR DIVIDIR …
Estas pinches convenciones solo «le convienen» a las grandes desarrolladoras, o cuando tienes equipos de trabajos grandes en proyectos grandes…
LA UNICA GRAN VENTAJA es que de esa manera es más fácil mantener el código cuando VARIOS PROGRAMADORES LE METERAN MANO.
Por eso para las grandes compañías que promueven esto, les conviene que los programadores se acostumbre a hacer TODO ESE TRABAJO DEMÁS, porque así ganarán las ventjas que ya he dicho. Y SE DE LO QUE HABLO PORQUE HE VISTO ESAS VENTAJAS DIRIGIENDO EQUIPOS DE DESARROLLO DE SOFTWARE en algunos proyectos.
Pero para proyectos medianos, en los que solamente yo soy el programador, y solamente yo le voy a dar mantenimiento a ese sistema. SE ME HACE MUCHO MÁS TRABAJO implementar todo ese rollo y trabajar bajo dominios, y se van haciendo CARPETAS Y CARPETAS….
Y lo explico: qué sentido tiene hacer un monton de carpetas para usar patrón repositorio, y un montón de carpetas para cada tipo de TRAIT, y cosas por el estilo… además eso ya se ve FEAMENTE ORGANIZADO EN TANTA «CARPETERÍA ANIDADA»
En resumen:
Como Gerente de Desarrollo y que vengo desde abajo con más de 15 años programando y COMO BIEN MENCIONAS EN ESTE POST … ES MÁS TRABAJO TRABAJAR ASÍ, pero si tu proyecto es grande y tienes varios programadores CONVIENE HACERLO!
Si tu proyectos es tuyo y solo tuyo para un cliente al cual solo tu le desarrollas y le das mantenimiento, a veces NO CONVIENE HACER MAS TRABAJO, sino simplemente tener bien organizadito todo.
Y sé que no va faltar el que venga a decir «que mal gerente eres y blablabla» por decir esto me van a odiar, PERO NO DIGO MAS QUE LA VERDAD aunque a algunos puristas les cale.
Simplemente digo que estas cosas tienen ventajas y desventajas, y como decimos en México «Según el sapo, es la pedrada», habrá proyectos en que conviene trabajar un poco más haciendo folders y reestructurando mucho el codigo, PERO LA REALIDAD ES QUE NO ES UNA LEY PARA TODOS O QUE TODOS TENGAN QUE SER ASÍ…Aunque me odien y critiquen los puristas.
Comparto! En proyectos chicos es mejor usar algo simple que te solucione el problema. Este blog esta hecho en wordpress 🤷🏻♂️
Este artículo esta dirigido a personas que tienen curiosidad sobre el patrón repository y para que se puede usar.
Saludos.