Domain-Driven Design en PHP: cómo transformar el caos en orden
\n\n¿Alguna vez has sentido que tu proyecto PHP crece como una bola de nieve? Al principio todo es sencillo: un par de controladores, un modelo, algunas consultas a la base de datos. Pero seis meses después, estás ahogado en código espagueti, donde la lógica de negocio está dispersa por servicios y un "simple" cambio de requisito te lleva una semana.
\n\n¿Te suena familiar? Bienvenido al club. Pero hay una buena noticia: Domain-Driven Design (DDD) no es una teoría abstracta de los libros de Eric Evans. Es una herramienta práctica que realmente te ayuda a escribir código PHP comprensible tanto para el negocio como para los desarrolladores.
\n\nEn este artículo, exploraremos DDD en la práctica. Sin rodeos, con ejemplos reales en PHP. Aprenderás cómo implementar DDD en tu proyecto sin volverte loco ni tener que reescribir todo desde cero. ¡Vamos allá!
\n\n¿Qué es Domain-Driven Design y por qué lo necesita un desarrollador PHP?
\n\nImagina que estás creando una tienda online. Tienes descuentos, carrito de compras, pedidos. La lógica de negocio dice: "el descuento no se acumula con el código promocional", "el pedido no se puede cancelar después del envío". Si guardas esta lógica en los controladores o la distribuyes por los modelos del ORM, tarde o temprano terminarás enredado.
\n\nDDD es un enfoque que sitúa el dominio del negocio (tu área de conocimiento) en el centro del desarrollo. En lugar de pensar en tablas y consultas SQL, piensas en entidades, valores y eventos.
\n\nPara PHP, esto es especialmente relevante. El lenguaje es flexible, pero sin una arquitectura clara, los proyectos se convierten rápidamente en "legado". DDD proporciona un esqueleto que sigue siendo legible incluso después de un año.
\n\nDiseño Estratégico vs. Diseño Táctico
\n\nDDD se divide en dos grandes partes:
\n- \n
- Diseño Estratégico — es el mapa de tu proyecto. Identificas contextos delimitados (Bounded Contexts) y construyes un lenguaje ubicuo (Ubiquitous Language). Por ejemplo, en el contexto "Catálogo", un producto es solo un conjunto de características, mientras que en el contexto "Pedido", es una unidad con precio y cantidad. \n
- Diseño Táctico — son los bloques de construcción: Entity, Value Object, Aggregate, Repository, Domain Service, Domain Event. Nos centraremos en estos. \n
Diseño Táctico en PHP: los bloques de DDD
\n\nVeamos cómo se ven los patrones principales de DDD en PHP. Olvídate de ActiveRecord de Laravel o Symfony: aquí construimos un modelo independiente del framework.
\n\n1. Entity (Entidad)
\nUna entidad es un objeto que tiene un identificador único y cambia con el tiempo. Por ejemplo, User o Order. Comparamos entidades por su ID, no por sus campos.
<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Domain\\Entity;\n\nuse App\\Domain\\ValueObject\\UserId;\nuse App\\Domain\\ValueObject\\Email;\n\nclass User\n{\n public function __construct(\n private UserId $id,\n private string $name,\n private Email $email\n ) {}\n\n public function getId(): UserId\n {\n return $this->id;\n }\n\n public function changeEmail(Email $newEmail): void\n {\n // Aquí puede ir lógica de negocio: verificar si el email ya está en uso\n $this->email = $newEmail;\n }\n\n public function getEmail(): Email\n {\n return $this->email;\n }\n}\n\n\n2. Value Object (Objeto de Valor)
\nUn Value Object es un objeto inmutable que se define por sus atributos, no por un identificador. Dos Value Objects son iguales si todos sus campos coinciden. Clásicos: Money, Email, Address.
<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Domain\\ValueObject;\n\nclass Email\n{\n private string $value;\n\n public function __construct(string $value)\n {\n if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {\n throw new \\InvalidArgumentException('Dirección de correo inválida');\n }\n $thi