De MVC a DDD Parte IV - Reorganizando las carpetas de MVC a estructura DDD incluyendo namespaces
Refactor de estructura de carpetas y namespaces
La rama correspondiente es esta:
git checkout blog-publish-mvc-v3
Originalmente contamos con la siguiente estructura de careptas MVC.
- controladores
- modelos
- servicios (agregada como capa de negocio)
- utils (donde guardamos clases de fines varios, equivalente al "framework")
- vistas (la salida del resultado de proceso)
├── public │ └── index.php └── src ├── Blog │ ├── Controllers │ │ └── PublishController.php │ ├── Models │ │ ├── PostEntity.php │ │ ├── Repositories │ │ │ ├── PostRepository.php │ │ │ └── UserRepository.php │ │ └── UserEntity.php │ ├── Services │ │ └── PostPublishService.php │ ├── Utils │ │ ├── Monolog.php │ │ ├── RequestTrait.php │ │ ├── ViewTrait.php │ │ └── monolog.log │ └── Views │ └── post-published.php └── autoload.php
Terminaremos obteniendo una estructura como esta:
├── public │ └── index.php └── src ├── Blog │ ├── Application │ │ ├── MonologService.php │ │ ├── NotifyService.php │ │ └── PostPublishService.php │ ├── Domain │ │ ├── PostEntity.php │ │ └── UserEntity.php │ ├── Infrastructure │ │ ├── Monolog.php │ │ ├── PublishController.php │ │ ├── Repositories │ │ │ ├── PostRepository.php │ │ │ └── UserRepository.php │ │ ├── RequestTrait.php │ │ └── ViewTrait.php │ └── Views │ └── post-published.php └── autoload.php
Antes de explicar como se hace la distribución vamos a fijarnos en la siguiente imagen. Notese la dirección de las flechas, estas indican dependencia.
Infraestructura depende de Aplicación, Aplicación de Dominio y Dominio de el mismo.
Capa de dominio:
Abarca todos los objetos estrechamente relacionados con el negocio. En el ejemplo, si nuestro negocio es un "Blog" lo más seguro es que se componga de los artículos y los autores.
En este caso PostEntity y UserEntity respectivamente.
Dependiendo del lenguaje ubicuo puede que en lugar de User tengamos que usar Author, eso lo define el propietario del producto (PO).
Para el ejemplo me quedo con User por ser más común.
Otros elementos que deberiamos incluir en esta sección son las excepciones, eventos de dominio, agregados, value objects e interfaces de buses de comando y eventos.
Asi tendriamos una namespace App\Blog\Domain con nuestras entidades y las interfaces de nuestros repositorios más no las implementaciones ya que estas últimas
son clases de entrada y salida de datos que se gestionan fuera del dominio, más concretamente en Infraestructura
Sobre las interfaces o puertos hablarémos más adelante.
El dominio solo depende de el mismo. Simplificando muy mucho podriamos decir que nuestro dominio son los modelos. (como mnemotécnico)
Capa de aplicación:
Alberga todas aquellas acciones que se deben que ejecutar para llevar a cabo nuestra lógica de negocio.
Un paso extra, para desacoplar más esta capa, es llevar parte del servicio único PostPublishService a otros dos servicios: MonologService y NotifyService.
Estos son potenciales candidatos a ser utilizados por otros servicios/controladores. Ahora se inyectan como dependencias.
En resumen. La capa de aplicación esta relacionada con los casos de uso. En el ejemplo:
- Publicar una entrada. (marcar su estado a 1)
- Guardar un log con el mensaje: "Post with title {$title} published by user {$emailTo}".
- Enviar un mensaje por email a quien lo ha publicado: "Your post with id {$postId} has been published".
Capa de Infraestructura:
Es la capa que gestiona la entrada y salida hacia hacia/de las otras capas. Aqui debemos alojar las implementaciones que nos permitiran interactuar con el dominio. En este caso los
repositorios. Que es muy común esten ligados a un bd.
En esta carpeta agrupamos: controladores, componentes del framework como: Monolog,RequestTrait y ViewTrait y quizas algún elemento más.
Vistazo rápido de como quedan los namespaces
<?php
namespace App\Blog\Infrastructure;
use App\Blog\Infrastructure\Repositories\UserRepository;
use App\Blog\Infrastructure\Repositories\PostRepository;
use App\Blog\Application\MonologService;
use App\Blog\Application\NotifyService;
use App\Blog\Application\PostPublishService;
use \Exception;
final class PublishController
{
use RequestTrait;
use ViewTrait;
public function publish(): void
{
$userId = $this->getRequestSession("userId", 1);
$postId = $this->getRequestPost("postId", 1);
<?php
namespace App\Blog\Application;
use App\Blog\Domain\PostEntity;
use App\Blog\Infrastructure\Repositories\PostRepository;
final class PostPublishService
{
private PostRepository $postRepository;
private NotifyService $notifyService;
private MonologService $monologService;
public function __construct(
PostRepository $postRepository,
NotifyService $notifyService,
MonologService $monologService
)
{
...
}
}
<?php
namespace App\Blog\Application;
use App\Blog\Infrastructure\Repositories\PostRepository;
use App\Blog\Infrastructure\Repositories\UserRepository;
use App\Blog\Infrastructure\Monolog;
final class MonologService
{
private UserRepository $userRepository;
private PostRepository $postRepository;
public function __construct(UserRepository $userRepository, PostRepository $postRepository)
{
...
}
Si ejecutamos la aplicación deberíamos ver las mismas trazas en amarillo y el contenido del post. Esto nos indica que el refactor no ha alterado la funcionalidad.
Dejo el video de CodelyTv donde tocan una parte de este tema: La responsabilidad de cada capa y los elementos que la componen.
Clean Architecture: La mejor forma de escalar y mantener tu código
Autor: Eduardo A. F.
Publicado: 29-04-2022 23:00