
Recentemente, no podcast Zinc Prod , meus amigos e eu discutimos o padrão CQRS / ES e alguns recursos de sua implementação no Elixir. Porque Eu uso o Laravel no meu trabalho, foi um pecado não me aprofundar na Internet e não descobrir como você pode saborear essa abordagem no ecossistema dessa estrutura.
Convido todos os envolvidos, tentei descrever o tópico da maneira mais abstrata possível.
Algumas definições
CQRS (Segregação de responsabilidade de consulta de comando) - alocação de operações de leitura e gravação em entidades separadas. Por exemplo, escrevemos para o mestre, lidos na réplica. CQRS. Fatos e conceitos errôneos - Ajuda a obter um conhecimento completo do Zen CQRS.
ES (Event Sourcing) - armazenamento de todas as alterações de estado de uma entidade ou conjunto de entidades.
O CQRS / ES é uma abordagem arquitetural na qual salvamos todos os eventos da mudança de estado de uma entidade na tabela de eventos e adicionamos um agregado e um projetor a isso.
Agregado - armazena na memória as propriedades necessárias para tomar decisões de lógica de negócios (para acelerar a gravação), toma decisões (lógica de negócios) e publica eventos.
Projetor - ouve eventos e grava em tabelas ou bancos de dados separados (para uma leitura mais rápida).

Em batalha
Projetor de eventos Laravel - biblioteca CQRS / ES para Laravel
O Larabank é um repositório com uma abordagem CQRS / ES. Vamos levá-lo a julgamento.
A configuração da biblioteca lhe dirá onde procurar e o que é. Nós olhamos para o arquivo event-projector.php . Do necessário para descrever o trabalho:
projectors
- registrar projetores;reactors
- registre reatores. Reator - nesta biblioteca adiciona efeitos colaterais ao processamento de eventos, por exemplo, neste repositório, se você tentar exceder o limite de retirada três vezes, o evento MoreMoneyNeeded é gravado e uma mensagem é enviada ao usuário sobre suas dificuldades financeiras;replay_chunk_size
- tamanho do pedaço de replay. Um dos recursos do ES é a capacidade de restaurar o histórico de eventos. O projetor de eventos do Laravel se preparou para um vazamento de memória durante essa operação usando essa configuração.
Preste atenção à migração. Além das tabelas padrão do Laravel, temos
stored_events
- a tabela ES principal com várias colunas de dados não estruturados para dados de meta eventos, armazenamos tipos de eventos em uma linha. Coluna importante aggregate_uuid
- armazena o uuid do agregado para receber todos os eventos relacionados a ele;accounts
- a tabela do projetor de contas de usuário, é necessária para o retorno rápido dos dados atuais sobre o estado do saldo;transaction_counts
- uma tabela do projetor do número de transações do usuário, necessária para o retorno rápido do número de transações concluídas.
E agora me proponho a pegar a estrada com um pedido para criar uma nova conta.
Criação de conta
O roteamento de resource
padrão descreve o AccountsController . Estamos interessados no método de store
public function store(Request $request) { $newUuid = Str::uuid();
AccountAggregateRoot herda a biblioteca AggregateRoot . Vejamos os métodos que o controlador chamou.
O método persist
chama o método storeMany
modelo especificado na configuração event-projector.php como stored_event_model
no nosso caso, StoredEvent
public static function storeMany(array $events, string $uuid = null): void { collect($events) ->map(function (ShouldBeStored $domainEvent) use ($uuid) { $storedEvent = static::createForEvent($domainEvent, $uuid); return [$domainEvent, $storedEvent]; }) ->eachSpread(function (ShouldBeStored $event, StoredEvent $storedEvent) {
* QueuedProjector
Os projetores AccountProjector e TransactionCountProjector implementam o Projector
portanto Projector
eles responderão aos eventos de forma síncrona com a gravação.
Ok, uma conta foi criada. Proponho considerar como o cliente o lerá.
Exibição de fatura
Se o projetor de contas implementar a interface QueuedProjector , o usuário não verá nada até que o evento seja processado por sua vez.
Por fim, estudaremos como funciona a reposição e retirada de dinheiro da conta.
Recarga e retirada
Mais uma vez, veja o AccountsController :
Considere AccountAggregateRoot
ao reabastecer a conta:
public function addMoney(int $amount) { $this->recordThat(new MoneyAdded($amount)); return $this; }
* AggregateRoot
ao retirar fundos:
public function subtractMoney(int $amount) { if (!$this->hasSufficientFundsToSubtractAmount($amount)) {

Conclusão
Tentei descrever o processo de “onboarding” no CQRS / ES no Laravel o mais livre de água possível. O conceito é muito interessante, mas não sem recursos. Antes de implementar, lembre-se de:
- consistência eventual;
- é desejável usar DDD em domínios separados, você não deve criar um sistema grande completamente nesse padrão;
- alterações no esquema da tabela de eventos podem ser muito dolorosas;
- De maneira responsável, vale a pena abordar a escolha da granularidade dos eventos, quanto mais eventos concretos houver, mais eles estarão na tabela e mais recursos serão necessários para trabalhar com eles.
Ficarei feliz em notar erros.