Desacoplando Laravel de tu aplicación

Desacoplando Laravel de tu aplicación

Deja una respuesta

Comment as a guest.

  1. Buen Artículo, excelente la explicación pero no entendí bien, pero por culpa mía no del profesor jaja me re intereso el tema pero siento que me falta leer bastante para lograr hacer algo así. Fijate que la parte de la bibliografia justo al menos yo no puedo verla

    1. Muchas gracias Mauro! Voy a mencionar los libros en el artículo porque algunas personas también ya me han dicho que no los pueden ver.

  2. Ciertamente cómo mencionas en el post, una de las contras de aplicar esta arquitectura es la complejidad que conlleva crearla, en lo personal me gusta usar sólo algunos de los conceptos del DDD como los objetos de valor, inmutabilidad, pero pienso que desacoplarse tanto del framework es a su vez desaprovecharlo porque se asume que dentro del diseño se debe contemplar el framework a usar, de todas formas, excelente post.

    1. Concuerdo! Y, como siempre, todo depende del contexto en el que este tu proyecto y el nivel de abstracción que quieras manejar.
      En este mismo ejemplo utilizo cosas de Laravel. Pero creo que lo importante es que tu dominio si este abstraído lo máximo posible del framework, de la base de datos, de servicios de 3ros, etc.
      Muchas gracias por tu comentario :).

  3. Excelente artículo!!!
    Tu solución es mucho más simple que la que hice yo cuando empecé a aplicar DDD con Laravel (5.6)

    Así que ahora me toca Refactoriar mucho!!!!

    Pero no me importa 😉
    Me gusta su solución!
    Enhorabuena!

    PD: Te comento sólo dos detalles (por empezar con algo 🙂
    1. Los id los proporciona la Request en lugar de ser autogenerados por el Repository
    2. La ejecucion de los comandos no devuelven nada.
    Opino que la comunicacion ascendente, desde el Dominio hacia la UI, debe estar resuelto tambien por la Arq Hexagonal.
    En mi caso, mis Handler sí que devuelven respuestas

    En fin, hay mucho que comentar 😉
    Un cordial saludo!

    1. Muchas gracias Javier por tu comentario. Con respecto a los detalles que marcas:
      1. Totalmente de acuerdo! Pero no quise extender el artículo con uuid desde el front y demás. Pero si, es correcto lo que marcas.
      2. En este caso es un «si y no». Si bien puse un return al método execute() del bus de comandos, el handler y el repositorio no esta devolviendo nada y el controlador tampoco espera nada de él.
      Por otro lado, yo creo que, tanto los patrones de diseño como las arquitecturas no deben ser implementadas a raja tabla. Tiene que existir una flexibilidad dependiendo el caso y la mejor solución para cada proyecto.

      Saludos bro!

  4. Hola excelente post la verdad me sirvio muchisimo para empezar a programar mejor, aun que tengo una duda por mi ignorancia cuando creas el modelo le pasas como parametro un ArticleEntity pero tu modelo es un Article, y usas el metodo del modelo directamente? No deberia ser un Article tambien en lugar de un Entity no se si se entiende mi duda. Gracias

  5. Buenísima la explicación. Difícil de entender en sus comienzos debido al cambio en como se deben pensar las cosas, es cuestión de releerlo varias veces e intentar llevarlo a la practica.
    Tengo una consulta, es respecto al modelo. Si quiero usar las relaciones que propone laravel, donde debo hacerlo? Así no rompo con: «todo lo que esta en la capa de dominio no debe tener relación con el exterior.»

    1. Muchas gracias por tu comentario Emilio.
      Todo lo que sea manejo con la persistencia de datos (ya sea consultas, relaciones, inserciones, etc.) deberías abstraerlo con los repositorios.
      Los repositorios son la capa intermedia que se comunican con los Modelos de Laravel (o con Doctrine, o con Redis, o con cualquier persistencia) y tiene lo métodos necesarios que le servirán al cliente que utilice el repositorio.
      Por ejemplo y basándonos en el ejemplo del artículo, supongamos que necesitas saber el autor de un artículo, entonces en el repositorio de artículos de Eloquent (ArticleEloquentRepository.php) harías:

      public function getAuthorFromArticle()
      {
      return $this->model->author->name;
      }

      Donde $this->model es una instancia del modelo Article. Y en Article.php existe la relación author().

      Espero que se entienda, si no vuelve a preguntar que no es molestia.
      Saludos.

      1. Perfecto, lo entendí. Muchas gracias. Me había generado dudas esto: «Como vemos, nuestra clase ArticleEntity no extiende ni implementa nada. Ni tampoco tiene ningún use esto debe ser así ya que, todo lo que esta en la capa de dominio no debe tener relación con el exterior.»

  6. Hola, excelente el articulo. Mi duda es saber donde incluyo validaciones propias del negocio? por ejemplo si no quiero permitir que se creen mas de 3 artículos por autor. Donde se incluye y como notificar a la capa mas alta?gracias

    1. Hola Eduardo, todas las reglas de negocio van en la capa de dominio. O sea, en las entidades. Si no cumplen las reglas, entonces lanzan una excepción para notificar a la capa de infraestructura.

      1. tomando una respuesta anterior. Seria valido escribir esta regla en el repositorio?
        //ArticleEloquentRepository.php

        public function exceeds_limit_articles() : bool
        {
        $limit = 3;
        $author = $this->model->author;
        if( $author->articles->count() >= $limit ){
        return true;
        }
        return false;
        }

        public function save( ArticleEntity $article ): void
        {
        if ( $this->exceeds_limit_articles() ){
        //..
        //throw new Exception
        }

        $this->model->create( $article );
        }
        Nuevamente Gracias

        1. No, cómo te comentaba las reglas van en la capa de dominio (para el caso que mencionas sería ArticleEntity o Article a secas). Fíjate que lo estás poniendo en ArticleEloquentRepository que es una clase concreta del repositorio que accede a la BD, por lo tanto, esta clase pertenece a la capa de infraestructura.

          Por lo tanto, en el hanlder (o en una nueva clase, cómo quieras) tenés que tener el repositorio que devuelva solo la cantidad de exceeds_limit_articles. Esta cantidad la deberías pasar a un método de ArticleEntity y este método debe resolver si devuelve true/false o la excepción. Y por último gravas la entidad con el repositorio.

  7. Buenas, me esta costando diferenciar entre el Article (model) y el ArticleEntity. Me podrias explicar este ultimo un poco mas por favor para entender la diferencia con el primero.

    1. Hola Emilio, como estas?

      A grandes rasgos, el modelo lo estamos dejando para que solo haga operaciones con la BD. Y la clase Entidad la utilizamos para que haga toda la lógica con los datos.

      Tal vez te genere un poco de confusión porque algunas personas suelen poner la lógica de la aplicación en los modelos. Y eso no esta mal. Pero en la arquitectura que comento en la publicación, queremos separar lo que es la lógica del negocio de la persistencia de los datos, cosa que siempre tengamos la libertad de sacar una implementación y utilizar otra.

      Si todavía tienes dudas, vuelve a comentar con más detalle para ayudarte mejor. Saludos.

  8. Estoy implementando la arquitectura y me surgieron un par de dudas.
    Primero, la clase CreateArticleHendler implemente la interface Hendler, la cual no esta definida dentro de este ejemplo, o yo no la estoy visualizando. Me podrías indicar como seria la misma?
    Lo segundo seria que en el caso de uso que estoy queriendo implementar es un endpoint donde llamo a otra api. Entonces, la llamada a esta segunda api, debe estar implementada en el Handler? Para este caso no haría falta un Repo verdad?

    1. Hola Emilio, como estás?
      En este ejemplo se podría llegar a obviar la interface del Handler pero no es mala idea ponerla (aunque no tenga ningún método) para «agrupar» todos los handlers.
      En este caso, el código de la interface del Handler, simplemente sería:


      interface Handler
      {
      }

      Con respecto a tu segunda pregunta, si le estas pegando a un endpoint para hacer operaciones de persistencia, entonces si, deberias implementar un repositorio como el que comento en el articulo pero en vez de ir a Eloquent, le pegue a la api.
      Eso es lo bueno de los repos, te permiten abstraer los datos de su fuente (si van o vienen de una bd, de una api, de la memoria, etc. le da lo mismo al repo).

      Espero que te sirva, cualquier cosa vuelve a preguntar.
      Saludos.

      1. La interface Handler iria dentro de «Application\Services\Contracts»?
        Por otra parte, me quedo clara la explicacion, aunque en mi caso, el api externa no la voy a usar para persistir en la base de datos, sino para realizar tareas de negocio y seguridad, como logueo, obtención de datos, etc. Igualmente deberían estar en el repo?

        1. Si, la interface iría ahí.
          Piensa que cada comportamiento es un caso de uso único y un caso de uso puede utilizar a otro. Entonces, volviendo a tu pregunta original, si el endpoint es un comportamiento entonces si debes meterlo en un caso de uso y tu handler (tu otro caso de uso) debe utilizarlo.

  9. Excelente Articulo! Muy bien explicado!
    Una duda, si quisiera implementar Validator, donde entraria? en los Repositorios?, ademas, si quisiera ejecutar varios comandos que insertan en DB, como manejar si el segundo comando falla? se hace rollback del primer comando?
    Muchas gracias por el aporte!

    1. Hola Yako, como estás? Muchas gracias por tu comentario y por darte una vuelta por el blog.
      La clase Validator pertenece al framework, por lo tanto iría en la capa de infraestructura. Por lo tanto, iría en el controlador como siempre. Pero, yo utilizaría los FormRequest.
      Con respecto al segundo punto que comentas, en realidad es un comando por caso de uso. Debes disparar un solo comando y este ejecutará un o varios casos de uso. Por ejemplo, si quisieras actualizar el sexo de un usuario primero debes buscar al usuario, validar que exista y luego actualizarlo.
      Entonces, el command para esto sería UpdateUserGenderCommand y esto activaría 2 casos de uso: UserFinder y UserUpdater. Si alguno de los dos casos de uso fallarían, dispararían su propia excepción que sería capturada por el controller que disparo el comando.

      Resumiendo, la idea es esa, disparar un comando y que se activen los casos de uso necesarios.
      Cualquier duda, no dudes en preguntar.
      Saludos.

  10. Hola matias gusto saludarte, podrias ayudarme con este cambio que le hice a la clase UserElquentReporitory para que me funcionara,
    // Método obligado a desarrollar por la implementación
    // de UserRepository
    public function save(UserEntity $user): void
    {

    $this->model->create([
    ‘name’ => $user->getName(),
    ‘email’ => $user->getEmail(),
    ‘password’ => $user->getPassword()
    ]);
    }
    original
    // Método obligado a desarrollar por la implementación
    // de UserRepository
    public function save(UserEntity $user): void
    {
    $this->model->create($user);
    }

  11. Se pueden comunicar entre dominos??
    Es decir, como en el ejemplo mencionado, si existiera un Modulo o Dominio llamado Usuarios y yo quisiera guardar quien creo el artículo dentro del Domino de artículos debo crear por ejemplo ArticuloAutorId o puedo utilizar UsuarioId del dominio de usuarios o el ArticuloAutorId se extendería de UsuarioId??

    1. Por el ejemplo que mencionas creo que te referis a si se pueden reutilizar clases, no? Si, usa directamente UsuarioId.

      Con respecto a la comunicación entre dominios, los dominios se deben que comunicar por medio de eventos (normalmente) asíncronos.
      Una entidad dispara un evento cuando termino de realizar una acción y las entidades interesadas se suscriben a ese evento.

  12. Buenas, te consulto. Estoy desacoplando una aplicación monolitica en varios microservicios y ahora tengo un caso de uso que requiere que uno de los datos lo valide mediante otro microservicio que ya he creado. La instancia de este MC para validar el dato entiendo no lo debo hacer en la clase entity. Entonces, debo resolver todo esto en el repo? Es ahí donde debo generar la instacia para llamar al metodo de validación? O la instancia la debo crear en el handler y pasársela al repo como parametro?

      1. Gracias mati por la respuesta. Es así el caso de uso: tengo que dar de alta un usuario que tiene una relación de integridad con una tabla que pertenece a otro microservicio. Este otro mocroservicio tiene una gerargia padre en cuanto a los datos. Por ende, para dar de alta a mi usuario, el valor que tengo que validar tiene que existir. Es una regla de negocio como vos decís. Creía que no debía ir esta validación ni en la entidad ni en el Value Object ya que, si bien es una validación de negocio, tengo que consultar un servicio externo, el cual debería estar definido en la capa de infraestructura, y tengo entendido que la capa de dominio no puede comunicarse con esta otra capa. Aun así debo ponerlo en la capa de dominio a esta validación?

        1. ¿Por qué un dato importante para crear un usuario vive en otro dominio? Cuando llegas a esa pregunta puede que la respuesta sea «porque ese otro dominio es parte del dominio de Usuarios». A lo que voy es que puede que haya funcionalidad en un microservicio que en realidad pertenezca a otro.
          Pero cuesta llegar a una conclusión porque no conozco el sistema.
          Pero bueno, lo que te puedo llegar a decir es que, si esa validación es responsabilidad de otro MS, entonces debería ser un caso de uso de ese MS.
          Por lo tanto, el caso de uso «alta de usuario» debería ejecutar el caso de uso «validación de dato».

  13. Hola me encanto el articulo, exlente explicacion me sirvio mucho para poder entender y realizarlo. pero tengo una duda y capaz vos ya lo tengas implemetado. como despacharias y ejecutarias los eventos?

    1. Hola Rodolfo, muchas gracias por tu comentario.
      Lo podes hacer con el dispatcher que viene en Laravel o usar un Command Bus como RabbitMQ o Kafka (una implementación conocida de estos command bus es Tactician).

  14. Estoy aplicando la arquitectura y resulta muy claro el código una vez que se entiende cada capa de la misma. La duda que estoy teniendo en estos momentos es en el repo, cuando devuelvo un recurso o un listado, tengo que usar un DTO de salida? Puedo usar los Api Resource de laravel? Este ultimo tengo que ponerlo en el repo? Entiendo que si los api resource los pongo en el handler y desde el repo devuelvo el modelo, estoy acoplando la implementación al uso de eloquent.

    1. Hola Emilio, como estas? Disculpas en la demora de la respuesta.
      Cuando devolves un listado de resultados, lo tenes que hacer en una Colección de DTOs o Entidades. Esto lo tiene que devolver el repositorio.
      Los Api Resources son justamente esto con algunos detalles que incorpora Laravel. Y no habría problema que los implementes en los repo ya que las dos cosas viven en la capa de infraestructura.

  15. Hola! Gracias por este post hermano, gracias a uds pude comprender como es lo basico para empezar mi proyecto usando esta arquitectura, no hay nada mejor como un ejemplo practico, donde vayas del pi al pa con todo lo necesario.

    Ahora lo que debo hacer es separar mi app por bounded context y aplicar tu explicacion.

    Una duda me quedó: Por que pusiste el container dentro de Bus? Será que todo lo que invocas en Infra es Commands? Quiza te haga falta mandar un mail, creo que poner Container en el namespace Bus no compagina ya que la interfaz Container tuya es la que debe usarse para que la implemente nuestro LaravelMailer no? De todas maneras si lo haces asi para este ejemplo en especifico me parece muy bien igual.

    Gracias por todos los tips.

    Saludos desde La Habana

  16. Muy buen artículo. Pero como sería si lo que queremos es hacer un GET a una url de una API externa? Es decir, nuestra fuente de datos sería la api, que nos devuelve la data. Pero si desde el controlador no podemos escupir el JSON desde donde lo hacemos? Y que escupimos desde el Controlador, dado que la respuesta ok o ko viene en el momento de ejecutar la consulta a la api?

    1. Hola Jose, como estas? Gracias por tu comentario.
      Como vos dijiste, la API es la fuente de datos por lo tanto vas a tener una implementación de repositorio para esa API. El repositorio de la api hará el get y si falla por algún motivo, dispararías una excepción que sería handleada en el controlador.

      Espero que se entienda, cualquier cosa volve a preguntarme.
      Saludos.

  17. Hola amigo como te va..?
    EL post me ha servido de gran ayuda, he visto el video web binar y note que creas carpetas por modulo es decir: src\Usuario\Application, Infrastructure, Domain y debo crear src\Blog\Application, Infrastructure, Domain

  18. Amigo me surgió otra duda viendo los videos y comparando el post en el constructor del controller del articulo inyectas una interfaces pero en los demás ejemplo no lo haces y el que «execute» es la capa de la aplicación, no debería ser la capa de infrastructure que «exucute» la comunicacion con la capa aplicacion, otra de mis preguntas es si hay varios modulo hay que registrar cada uno en el provider, al igual que el repository..?

    1. Hola Erick, como estas? Muchas gracias por tu comentario!
      Lo ideal es inyectar por interfaz, en estos momentos no me acuerdo el ejemplo donde inyecte la clase concreta pero si tienes a mano el ejemplo avísame por favor así comento porque lo hice así.
      La pregunta con respecto a «execute» creo que no la termine de entender. Me explicarías más?
      Con respecto a la última pregunta, si, hay que registrar todo en el service provider.

      Saludos!

  19. Hola, cómo estás. Está excelente el artículo y también el video que subiste a youtube.
    Me preguntaba si quizá podías explicarme un poco cómo creaste el commandBus, sigo sin entenderlo. Gracias de antemano

    1. Muchas gracias Santiago. Voy a ver si hago un artículo explicando el tema del command bus que es una duda que tiene varias personas.

Sliding Sidebar

Matias Echazarreta

¡Hola!

Mi nombre es Matias Echazarreta.
Soy desarrollador web con más de 12 años de experiencia. Amante de Laravel, de los libros y del rock de los ’90. Te puedes comunicar conmigo  por trabajos de contratación, haciendo click aquí.

Nuestro Patreon

Desde Patreon puedes solicitar asesoria personalizado. ¡Ir a Patreon!

Suscríbete a nuestra lista de correo