Más allá de CRUDs: [01] Laravel orientado al Dominio

Más allá de CRUDs: [01] Laravel orientado al Dominio

Los seres humanos pensamos en categorías, nuestro código debería ser un reflejo de eso.


Este es el artículo #01 originalmente publicado por Brent en su blog (puedes encontrar ahí la serie en su idioma original).

El índice de artículos que conforman esta serie lo puedes encontrar aquí.

Dicho esto, comencemos 😉.


El término “Dominio” lo tomo del popular paradigma de programación DDD, o “Diseño Orientado al Dominio (Domain Driven Design). Según una de sus definiciones se describe como “Esfera específica de actividad o conocimiento”.

Mientras que mi uso del término “dominio” no significa lo mismo que cuando se usa dentro del contexto de DDD, hay muchas similitudes. Si estás familiarizado con DDD, descubrirás estas similitudes a lo largo de esta serie. He tratado de especificar en los casos relevantes cualquier coincidencia y diferencia según sea el caso.

Entonces, dominios. También podría llamarlos “grupos”, “módulos”; Algunas personas los llaman “servicios”. Cualquiera que sea el nombre que prefieras, los dominios describen un conjunto de problemas de negocio que estás tratando de resolver.

Un momento.. me doy cuenta de que acabo de usar mi primer término “empresarial” en esta serie: “el problema de negocio”. Al leer esta serie, notarás que hice todo lo posible para alejarme del aspecto teórico, de alta gerencia y de negocios. Soy desarrollador y prefiero mantener las cosas prácticas. Entonces, otro nombre más simple sería “proyecto”.

Pongamos un ejemplo: una aplicación para gestionar reservas de hotel. Tiene que gestionar clientes, reservas, facturas, inventarios de hoteles, etc.

Los frameworks web modernos te enseñan a tomar un grupo de conceptos relacionados y dividirlo en varios lugares a lo largo de su base de código: controladores con controladores, modelos con modelos, etc. Ya entiendes la idea.

¿Alguna vez un cliente te dijo que “trabajes en todos los controladores ahora” o que “pases algo más de tiempo en el directorio de modelos”? No, te piden que trabajes en funciones de facturación, gestión de clientes o reservas.

Estos grupos son lo que yo llamo dominios. Apuntan a agrupar -dentro del proyecto- conceptos que se relacionan. Si bien esto puede parecer trivial al principio, es más complicado de lo que piensas. Es por eso que parte de esta serie se centrará en un conjunto de reglas y prácticas para mantener tu código bien ordenado.

Obviamente no hay una fórmula matemática que pueda darte, casi todo depende del proyecto específico en el que estés trabajando. Así que no pienses en esta serie como un conjunto de reglas fijas. Más bien piensa que te entrega una colección de ideas que puede usar y desarrollar, como quieras.

Es una oportunidad de aprendizaje, mucho más que una solución que puedas arrojar a cualquier problema que encuentres.

Dominios y aplicaciones

Si estamos agrupando ideas, evidentemente surge la pregunta: ¿hasta dónde llegamos? Por ejemplo, podría agrupar todo lo relacionado con la factura: modelos, controladores, recursos, reglas de validación, trabajos, etc.

Esto plantea un problema en las aplicaciones HTTP tradicionales: a menudo no existe un mapeo uno a uno entre los controladores y los modelos. De acuerdo, en las API REST -y para la mayoría de sus controladores CRUD clásicos- puede haber una asignación estricta uno a uno, pero desafortunadamente estas son las excepciones a las reglas que nos harán pasar un mal rato. Las facturas, por ejemplo, simplemente no se manejan de forma aislada, necesitan que se envíe un cliente, necesitan reservas para facturar, etc.

Es por eso que necesitamos hacer una distinción adicional entre lo que es el código de dominio y lo que no.

Por un lado, está el dominio, que representa toda la lógica empresarial; y, por otro lado, tenemos el código que usa/consume ese dominio para integrarlo con el framework y lo expone al usuario final. Las aplicaciones proporcionan la infraestructura para que los usuarios finales usen y manipulen el dominio de una manera fácil de usar.

En la práctica

Entonces, ¿cómo se ve esto en la práctica? El dominio tendrá clases como modelos, constructores de consultas, eventos de dominio, reglas de validación y más; Veremos en profundidad todos estos conceptos.

La capa de aplicación contendrá una o varias aplicaciones. Cada aplicación se puede ver como una aplicación aislada que puede usar todo el dominio. En general, las aplicaciones no se hablan entre sí.

Un ejemplo podría ser un panel de administración HTTP, y otro podría ser una API REST. También me gusta pensar en la consola, el artisan de Laravel, como una aplicación propia.

Como una descripción general de alto nivel, así es como se vería la estructura de carpetas de un proyecto orientado al dominio:

// Un directorio específico de dominio por concepto de negocio
app/Domain/Invoices
Actions
QueryBuilders
Collections
DataTransferObjects
Events
Exceptions
Models
Listeners
Rules
States

app/Domain/Customers
...

// El app panel de administración
app/App/Admin
Controllers
Middlewares
Requests
Resources
ViewModels

// La REST API app
app/App/Api
Controllers
Middlewares
Requests
Resources

// La app de consola
app/App/Console
Commands

Acerca de los namespaces

Es posible que hayas notado que el ejemplo anterior no sigue la convención de Laravel de \App como el namespace raíz único. Dado que las aplicaciones son solo una parte de nuestro proyecto, y debido a que pueden haber varias, no tiene sentido usar \App como la raíz de todo.

Ten en cuenta que si prefieres permanecer más cerca de la estructura predeterminada de Laravel, puedes hacerlo. Esto significa que terminarás con namespaces como \App\Domain y \App\Api. Pero eres libre de hacer lo que te resulte cómodo.

Sin embargo, si deseas separar los namespaces raíz, deberás realizar algunos cambios en la forma en que Laravel se inicia.

En primer lugar, deberás registrar todos los namespace raíz en tu composer.json:

{
    // …

    "autoload" : {
        "psr-4" : {
            "App\\" : "app/App/",
            "Domain\\" : "app/Domain/",
            "Support\\" : "app/Support/"
        }
    }
}

Ten en cuenta que también tengo un namespace \Support, que por ahora puedes considerar como el depósito de todos los pequeños helpers que no pertenecen a ningún lado.

A continuación, debemos volver a registrar el namespace \App, ya que Laravel lo usará internamente para varias cosas.

namespace App;

use Illuminate\Foundation\Application as LaravelApplication;

class BaseApplication extends LaravelApplication
{
    protected $namespace = 'App\\';
    
    public function path($path = '')
    {
        return $this->basePath.DIRECTORY_SEPARATOR.'app/App'.($path ? DIRECTORY_SEPARATOR.$path : $path);
    }
}

Finalmente, necesitamos usar nuestra aplicación base personalizada registrándola en bootstrap/app.php:

// bootstrap/app.php

$app = new App\BaseApplication(
    realpath(__DIR__.'/../')
);

Desafortunadamente, no hay una forma más limpia de hacer esto, ya que el framework nunca tuvo la intención de cambiar la estructura de carpetas predeterminada. Nuevamente, si no te sientes cómodo al hacer estos cambios, no dudes en seguir usando la estructura de namespaces raíz predeterminada de Laravel.

Cualquiera sea la estructura de carpetas que termines utilizando, lo más importante es que comiences a pensar en grupos de conceptos de negocio relacionados, en lugar de en grupos de código con las mismas propiedades técnicas.

Sin embargo, dentro de cada grupo, cada dominio, hay espacio para estructurar el código de manera que sea fácil de usar dentro de esos grupos individuales. La primera parte de esta serie analizará de cerca cómo se pueden estructurar los dominios internamente y qué patrones se pueden utilizar para ayudarlo a mantener su base de código mantenible a medida que crece con el tiempo. Después de eso, veremos la capa de aplicación, cómo se puede consumir el dominio exactamente y cómo mejoramos los conceptos existentes de Laravel utilizando, por ejemplo, modelos de vista.

Hay mucho terreno por recorrer, y espero que puedas aprender muchas cosas que puedas poner en práctica de inmediato.

2019-2024 © kennyhorna.com | @kennyhorna