Experto en desarrollo web y gestión de equipos técnicos, me especializo en la creación y optimización de soluciones digitales de alto rendimiento. Gracias a un profundo dominio de tecnologías modernas como React.js, Node.js, TypeScript, Symfony, Docker y FrankenPHP, garantizo el éxito de proyectos SaaS complejos, desde el diseño hasta la implementación, para empresas de diversos sectores.
¿Escuchas hablar de CQRS por todas partes pero te preguntas cómo aplicarlo sin tirar de Symfony o Laravel? Buena noticia: podemos explorar la Command Query Responsibility Segregation en PHP puro con solo una organización clara y unas cuantas clases bien pensadas. Abre tu editor, asentamos las bases juntos.
Requisitos previos: PHP 8.4 y configuración mínima
PHP 8.4 instalado (CLI o servidor web). Comprueba tu versión con para aprovechar las propiedades , los tipos estrictos y el soporte nativo de los first-class callables.
Composer (opcional pero práctico para el autoloading) y un servidor web ligero ( basta para experimentar).
Si debes quedarte en una versión anterior, asegúrate de que las características usadas aquí (promoción de constructores, propiedades , etc.) estén disponibles o adapta los fragmentos.
1. CQRS en dos minutos
CQRS consiste en separar la lectura (Query) de la escritura (Command). Obtienes dos caminos optimizados:
Los comandos mutan el estado (crear una cuenta, publicar un artículo) y no devuelven datos de aplicación.
Las consultas (queries) leen el estado (listar artículos, obtener el detalle de una cuenta) sin efectos secundarios.
Esta separación nace de la filosofía DDD, pero puedes adoptarla sin todo el formalismo. Beneficios inmediatos:
Tests más sencillos: pruebas handlers unitarios.
Código más legible: cada handler hace una sola cosa.
Escalabilidad progresiva: puedes hacer evolucionar el lado lectura de forma independiente del lado escritura.
Recuerda que CQRS añade algo de complejidad estructural. En un CRUD básico puede resultar demasiado. Úsalo cuando las reglas de negocio o las necesidades de escalabilidad lo justifiquen.
2. Estructura de carpetas mínima en PHP nativo
Podemos mantenernos ligeros con un árbol simple:
Distinguimos Domain (reglas de negocio), Application (casos de uso) e Infrastructure (implementaciones concretas).
3. Modelo de dominio minimalista
Creemos un agregado y su repositorio:
4. Lado Command: crear un artículo
El comando transporta la intención. El handler orquesta la lógica de negocio.
Nota importante: el handler no devuelve nada. Si quieres volver a leer el artículo, pasarás por una consulta (Query).
5. Lado Query: listar los artículos
Exponemos aquí un array listo para serializarse, sin depender del modelo de dominio en la capa de presentación.
6. Un repositorio en memoria para empezar
Podrás reemplazar esta implementación por una versión con Doctrine, PDO o una API externa más adelante, sin tocar los handlers.
7. Monta buses ultraligeros
El patrón CQRS se vuelve agradable cuando centralizas la resolución de handlers:
php
php
Estos buses siguen siendo ingenuos pero bastan para comprender la mecánica. En un proyecto real podrías conectarlos a un contenedor de inyección de dependencias (PHP-DI, Symfony DI, etc.).
8. Ejemplo de arranque en public/index.php
php
El operador (...) (first-class callable) disponible desde PHP 8.1 es totalmente compatible con PHP 8.4. Si debes soportar una versión anterior (8.0 o menos), sustitúyelo por fn ($command) => $createHandler($command).
9. Avanza poco a poco
Una vez que esta base está en su sitio, puedes enriquecer tu solución:
Validación: añade Value Objects (Título, Contenido) o una validación con Symfony Validator antes de crear tu agregado.
Persistencia real: implementa un repositorio MySQL (PDO) o PostgreSQL sin tocar los handlers.
Eventos de dominio: publica un ArticlePublished cuando el comando tenga éxito y consúmelo en otra capa.
Proyección dedicada: crea una tabla optimizada para lectura para consultas exigentes (queries) como la paginación o la búsqueda full-text.
Tests: escribe tests unitarios para cada handler y tests funcionales para los buses.
10. Checklist para tu primera puesta en producción
[ ] Cada comando tiene un handler dedicado que no devuelve nada.
[ ] Cada consulta está aislada y formatea los datos para el front.
[ ] Las dependencias técnicas se inyectan desde el exterior.
[ ] Los errores de negocio se gestionan en el dominio (excepciones especializadas, Value Objects, etc.).
[ ] Los tests cubren comandos, consultas y buses.
Con este esqueleto puedes introducir CQRS progresivamente en un proyecto existente o arrancar un nuevo servicio sin framework. La clave es mantener clara la frontera lectura/escritura y hacer evolucionar tu arquitectura por iteraciones. ¡Feliz experimentación!