CQRS e Event Sourcing em PHP: Arquitetura para Sistemas Complexos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Quando o CRUD tradicional não funciona mais: Conhecendo CQRS e Event Sourcing

\n\n

Você já passou pela situação em que sua aplicação PHP crescia, crescia... e de repente aquele UserController se transformava em um monstro de 2000 linhas? Familiar? Bugs se multiplicam, a performance cai, e adicionar uma nova funcionalidade causa um tique nervoso em toda a equipe. Parabéns, você chegou ao ponto em que é hora de adotar padrões arquiteturais sérios.

\n\n

Hoje vamos falar sobre duas ferramentas poderosas — CQRS (Command Query Responsibility Segregation) e Event Sourcing. Não se assuste com os termos complicados. Na verdade, são apenas maneiras elegantes de dizer: "Vamos parar de misturar leitura e escrita de dados" e "Vamos armazenar não o estado atual, mas o histórico de mudanças".

\n\n

Este artigo é para desenvolvedores PHP que já dominam os fundamentos de OOP e querem elevar seus projetos ao próximo nível. Você aprenderá como aplicar CQRS e Event Sourcing na prática, verá código real e entenderá quando esses padrões são realmente necessários, e quando é melhor evitá-los.

\n\n

O que é CQRS? Separando Comandos e Consultas

\n\n

Imagine que seu código é um restaurante. Normalmente, o garçom (controller) recebe o pedido (solicitação de escrita), corre para a cozinha (banco de dados), prepara o prato (processa os dados) e o serve imediatamente (retorna a resposta). Tudo funciona perfeitamente enquanto o restaurante tem 10 pessoas. Mas quando chegam 1000 clientes, a cozinha começa a sufocar.

\n\n

CQRS (Command Query Responsibility Segregation) propõe separar as responsabilidades: alguns cozinheiros (comandos) apenas preparam, outros garçons (consultas) apenas servem. Tecnicamente, isso significa que você usa modelos diferentes para leitura e escrita de dados.

\n\n

Implementação básica de CQRS em PHP

\n\n

Vamos ver como isso fica no código. Começaremos com um exemplo simples sem frameworks para entender a essência.

\n\n
// Comando — um DTO com dados para alterar o estado\nclass CreateUserCommand {\n    public function __construct(\n        public readonly string $email,\n        public readonly string $name\n    ) {}\n}\n\n// Manipulador do comando — aqui ocorre a lógica de negócio\nclass CreateUserHandler {\n    public function __construct(\n        private UserRepository $repository,\n        private EventDispatcher $dispatcher\n    ) {}\n\n    public function handle(CreateUserCommand $command): void {\n        $user = new User($command->email, $command->name);\n        $this->repository->save($user);\n        $this->dispatcher->dispatch(new UserCreatedEvent($user->getId()));\n    }\n}\n\n// Consulta — um DTO para obter dados\nclass GetUserQuery {\n    public function __construct(\n        public readonly int $userId\n    ) {}\n}\n\n// Manipulador da consulta — apenas leitura, nenhuma alteração\nclass GetUserHandler {\n    public function __construct(\n        private UserReadRepository $readRepository\n    ) {}\n\n    public function handle(GetUserQuery $query): ?UserDTO {\n        return $this->readRepository->findById($query->userId);\n    }\n}
\n\n

Observe: temos dois repositórios diferentes. UserRepository é responsável pela escrita (pode usar lógica de negócio complexa, validação), enquanto UserReadRepository é para leitura (pode ser uma simples consulta SQL ou até uma tabela desnormalizada).

\n\n

Barramento de Comandos (Command Bus)

\n\n

Em projetos reais, comandos e consultas são enviados através de um barramento — um despachante central que encontra automaticamente o manipulador adequado.

\n\n
// Implementação simples de Command Bus\nclass SimpleCommandBus {\n    public function __construct(\n        private array $handlers = []\n    ) {}\n\n    public function registerHandler(string $commandClass, object $handler): void {\n        $this->handlers[$commandClass] = $handler;\n    }\n\n    public function dispatch(object $command): void {\n        $handler = $this->handlers[$co                

Blogs

Book Recommendations