Digamos que cada vez que hago una operación CRUD o modifico una relación de una manera específica, también quiero hacer otra cosa. Por ejemplo, cada vez que alguien publica una publicación, también quiero guardar algo en una tabla para análisis. Quizás no sea el mejor ejemplo, pero en general hay mucha de esta funcionalidad "agrupada".
Normalmente veo este tipo de lógica en los controladores. Eso está muy bien hasta que quieras reproducir esta funcionalidad en muchos lugares. Cuando comienzas a entrar en parciales, crear una API y generar contenido ficticio, se convierte en un problema para mantener las cosas SECAS.
Las formas en que he visto administrar esto son eventos, repositorios, bibliotecas y agregar a modelos. Aquí están mis entendimientos de cada uno:
Servicios: aquí es donde la mayoría de las personas probablemente pondrían este código. Mi principal problema con los servicios es que a veces es difícil encontrar una funcionalidad específica en ellos y siento que se olvidan cuando las personas se centran en usar Eloquent. ¿Cómo sabría que necesito llamar a un método publishPost()
en una biblioteca cuando puedo hacerlo $post->is_published = 1
?
La única condición en la que veo que esto funciona bien es si SOLO usa los servicios (e idealmente hace que Eloquent sea inaccesible de alguna manera desde todos los controladores).
En última instancia, parece que esto solo crearía un montón de archivos innecesarios adicionales si sus solicitudes generalmente siguen la estructura de su modelo.
Repositorios: por lo que entiendo, esto es básicamente como un servicio, pero hay una interfaz para que pueda cambiar entre ORM, que no necesito.
Eventos: veo esto como el sistema más elegante en cierto sentido porque sabe que los eventos de su modelo siempre se llamarán con métodos Eloquent, por lo que puede escribir sus controladores como lo haría normalmente. Sin embargo, puedo ver que estos se vuelven desordenados y si alguien tiene ejemplos de proyectos grandes que usan eventos para el acoplamiento crítico, me gustaría verlo.
Modelos: Tradicionalmente, tenía clases que realizaban CRUD y también manejaban el acoplamiento crítico. Esto realmente facilitó las cosas porque sabía que todas las funcionalidades relacionadas con CRUD +, lo que sea que tuviera que hacer con él, estaban allí.
Simple, pero en la arquitectura MVC esto normalmente no es lo que veo hecho. En cierto sentido, prefiero esto a los servicios, ya que es un poco más fácil de encontrar y hay menos archivos de los que hacer un seguimiento. Sin embargo, puede ser un poco desorganizado. Me gustaría escuchar las desventajas de este método y por qué la mayoría de las personas no parecen hacerlo.
¿Cuáles son las ventajas / desventajas de cada método? ¿Me estoy perdiendo de algo?
fuente
Respuestas:
Creo que todos los patrones / arquitecturas que presenta son muy útiles siempre que siga los principios de SOLID .
Para saber dónde agregar lógica , creo que es importante hacer referencia al Principio de responsabilidad única . Además, mi respuesta considera que estás trabajando en un proyecto mediano / grande. Si es un proyecto de tirar algo en una página , olvide esta respuesta y agréguela a los controladores o modelos.
La respuesta corta es: dónde tiene sentido para usted (con servicios) .
La respuesta larga:
Controladores : ¿Cuál es la responsabilidad de los Controladores? Claro, puede poner toda su lógica en un controlador, pero ¿es esa la responsabilidad del controlador? No lo creo.
Para mí, el controlador debe recibir una solicitud y devolver datos y este no es el lugar para poner validaciones, llamar a métodos db, etc.
Modelos : ¿Es este un buen lugar para agregar lógica como enviar un correo electrónico de bienvenida cuando un usuario se registra o actualiza el recuento de votos de una publicación? ¿Qué sucede si necesita enviar el mismo correo electrónico desde otro lugar en su código? ¿Creas un método estático? ¿Qué pasa si ese correo electrónico necesita información de otro modelo?
Creo que el modelo debería representar una entidad. Con laravel, sólo utilizar la clase modelo para agregar cosas como
fillable
,guarded
,table
y las relaciones (esto es porque uso el patrón de repositorio, de lo contrario el modelo también tendría lossave
,update
,find
métodos, etc).Repositorios (patrón de repositorio) : Al principio estaba muy confundido por esto. Y, como usted, pensé "bueno, uso MySQL y eso es todo".
Sin embargo, he equilibrado los pros y los contras de usar el Patrón de repositorio y ahora lo uso. Creo que ahora , en este mismo momento, solo necesitaré usar MySQL. Pero, si dentro de tres años necesito cambiar a algo como MongoDB, la mayor parte del trabajo está hecho. Todo a expensas de una interfaz adicional y a
$app->bind(«interface», «repository»)
.Eventos ( patrón de observador ): los eventos son útiles para cosas que se pueden lanzar en cualquier clase en cualquier momento dado. Piense, por ejemplo, en enviar notificaciones a un usuario. Cuando lo necesita, activa el evento para enviar una notificación a cualquier clase de su aplicación. Luego, puede tener una clase como
UserNotificationEvents
esa que maneje todos sus eventos activados para las notificaciones de los usuarios.Servicios : hasta ahora, tiene la opción de agregar lógica a los controladores o modelos. Para mí, tiene sentido agregar la lógica dentro de los Servicios . Seamos realistas, Servicios es un nombre elegante para las clases. Y puede tener tantas clases como tenga sentido dentro de su aplicación.
Tome este ejemplo: Hace poco desarrollé algo así como los Formularios de Google. Empecé con una
CustomFormService
y terminó conCustomFormService
,CustomFormRender
,CustomFieldService
,CustomFieldRender
,CustomAnswerService
yCustomAnswerRender
. ¿Por qué? Porque tenía sentido para mí. Si trabaja con un equipo, debe poner su lógica donde tenga sentido para el equipo.La ventaja de usar Servicios vs Controladores / Modelos es que no está limitado por un solo Controlador o un solo Modelo. Puede crear tantos servicios como sea necesario según el diseño y las necesidades de su aplicación. Agregue a eso la ventaja de llamar a un Servicio dentro de cualquier clase de su aplicación.
Esto es largo, pero me gustaría mostrarle cómo he estructurado mi aplicación:
Yo uso cada carpeta para una función específica. Por ejemplo, el
Validators
directorio contiene unaBaseValidator
clase responsable de procesar la validación, basada en$rules
y$messages
de validadores específicos (generalmente uno para cada modelo). Podría poner fácilmente este código dentro de un Servicio, pero tiene sentido para mí tener una carpeta específica para esto, incluso si solo se usa dentro del servicio (por ahora).Le recomiendo que lea los siguientes artículos, ya que podrían explicarle las cosas un poco mejor:
Rompiendo el molde por Dayle Rees (autor de CodeBright): Aquí es donde lo armé todo, a pesar de que cambié algunas cosas para satisfacer mis necesidades.
Desacoplar su código en Laravel utilizando repositorios y servicios de Chris Goosey: esta publicación explica bien qué es un servicio y el patrón de repositorio y cómo encajan entre sí.
Los Laracasts también tienen los Repositorios de responsabilidad simple y simplificada, que son buenos recursos con ejemplos prácticos (aunque tenga que pagar).
fuente
Quería publicar una respuesta a mi propia pregunta. Podría hablar sobre esto durante días, pero voy a tratar de publicar esto rápidamente para asegurarme de que lo levante.
Terminé utilizando la estructura existente que proporciona Laravel, lo que significa que mantuve mis archivos principalmente como Modelo, Vista y Controlador. También tengo una carpeta de Bibliotecas para componentes reutilizables que no son realmente modelos.
NO ENVOLVÉ MIS MODELOS EN SERVICIOS / BIBLIOTECAS . Todas las razones proporcionadas no me convencieron al 100% del beneficio de usar los servicios. Si bien puedo estar equivocado, por lo que puedo ver, solo resultan en toneladas de archivos adicionales casi vacíos que necesito crear y cambiar cuando trabajo con modelos y también reducen realmente el beneficio de usar elocuencia (especialmente cuando se trata de RECUPERAR modelos , p. ej., utilizando paginación, ámbitos, etc.).
Puse la lógica comercial EN LOS MODELOS y accedo directamente desde mis controladores elocuentes. Utilizo una serie de enfoques para asegurarme de que no se omita la lógica empresarial:
Abordar las preocupaciones de las personas con el uso de modelos:
Nota adicional: Siento que envolver sus modelos en servicios es como tener una navaja suiza, con muchas herramientas, y construir otra navaja alrededor que básicamente haga lo mismo. Sí, a veces es posible que desee cortar con cinta una cuchilla o asegurarse de que dos cuchillas se usen juntas ... pero generalmente hay otras formas de hacerlo ...
CUANDO UTILIZA LOS SERVICIOS : Este artículo articula muy bien GRANDES ejemplos de cuándo usar los servicios ( pista: no es muy frecuente ). Básicamente, dice que cuando su objeto usa múltiples modelos o modelos en partes extrañas de su ciclo de vida , tiene sentido. http://www.justinweiss.com/articles/where-do-you-put-your-code/
fuente
Lo que suelo hacer para crear la lógica entre controladores y modelos es crear una capa de servicio . Básicamente, este es mi flujo para cualquier acción dentro de mi aplicación:
Así es como lo hago:
Este es el método de un controlador para crear algo:
Esta es la clase de servicio que realiza la lógica relacionada con la operación:
Y este es mi modelo:
Para obtener más información sobre esta forma, utilizo para organizar mi código para una aplicación Laravel: https://github.com/rmariuzzo/Pitimi
fuente
$congregation->save();
entonces tal vez no necesitaría repositorios. Sin embargo, es posible que vea que sus necesidades de acceso a datos aumentan con el tiempo. Puede comenzar a tener necesidades para$congregation->destroyByUser()
o$congregationUsers->findByName($arrayOfSelectedFields);
etc. ¿Por qué no desvincular sus servicios de las necesidades de acceso a datos? Deje que el resto de su aplicación trabaje con objetos / matrices devueltos por repositorios, y solo maneje la manipulación / formateo / etc ... Sus repositorios crecerán (pero los dividirá en diferentes archivos, en última instancia, la complejidad de un proyecto debe residir en algún lugar).En mi opinión, Laravel ya tiene muchas opciones para que almacene su lógica de negocios.
Respuesta corta:
Request
objetos de laravel para validar automáticamente su entrada y luego persista los datos en la solicitud (cree el modelo). Como todas las entradas de los usuarios están directamente disponibles en la solicitud, creo que tiene sentido realizar esto aquí.Job
objetos de laravel para realizar tareas que requieren componentes individuales, luego simplemente envíelos. Creo queJob
abarcan clases de servicio. Realizan una tarea, como la lógica empresarial.Respuesta larga (er):
Use repositorios cuando sea necesario: los repositorios están obligados a sobrebloquearse, y la mayoría de las veces, simplemente se usan como un
accessor
modelo. Siento que definitivamente tienen algún uso, pero a menos que esté desarrollando una aplicación masiva que requiera esa cantidad de flexibilidad para poder deshacerse por completo de laravel, manténgase alejado de los repositorios. Te lo agradecerás más tarde y tu código será mucho más sencillo.Pregúntese si existe la posibilidad de que vaya a cambiar los marcos PHP o a un tipo de base de datos que no sea compatible con laravel.
Si su respuesta es "Probablemente no", no implemente el patrón de repositorio.
Además de lo anterior, no coloque un patrón encima de un ORM excelente como Eloquent. Solo está agregando complejidad que no es necesaria y no lo beneficiará en absoluto.
Utilice los servicios con moderación: las clases de servicios para mí son solo un lugar para almacenar la lógica empresarial para realizar una tarea específica con sus dependencias dadas. Laravel los tiene listos para usar, llamados 'Empleos', y tienen mucha más flexibilidad que una clase de Servicio personalizada.
Siento que Laravel tiene una solución completa para el
MVC
problema lógico. Es solo una cuestión u organización.Ejemplo:
Solicitud :
Controlador :
En el ejemplo anterior, la entrada de la solicitud se valida automáticamente, y todo lo que necesitamos hacer es llamar al método de persistencia y pasar una nueva publicación. Creo que la legibilidad y la facilidad de mantenimiento siempre deben superar los patrones de diseño complejos e innecesarios.
Luego puede utilizar exactamente el mismo método de persistencia para actualizar las publicaciones, ya que podemos verificar si la publicación ya existe o no y realizar una lógica alternativa cuando sea necesario.
fuente
ShouldQueue
que proporciona Laravel. Si desea escribir lógica empresarial en un comando o evento, simplemente active el trabajo dentro de esos eventos / comandos. Los trabajos de Laravels son extremadamente flexibles, pero al final son simples clases de servicio.