CQS Pattern - Quando, como e por quê utilizar?
Tags: php, beginners, design, programming
Published: 2022-04-29 17:29:54

## Introdução

Este post tem como objetivo fazer um resumo sobre o que aprendi sobre Command Query Separation (CQS) e compartilhar com outras pessoas que estejam pesquisando sobre esse assunto. 
Eu particularmente tenho uma certa dificuldade em definir a responsabilidade de cada uma da minhas classes quando estou levando em consideração o [Principio da Responsabilidade Única](https://en.wikipedia.org/wiki/Single-responsibility_principle) e o que tem me ajudado a organizar melhor essas responsabilidades é entender como outros programadores mais experientes fazem isso. E durante uma sessão de estudos fui apresentado ao Command Query Segregation (CQS) um pattern que foi introduzido por [Bertrand Meyer](https://en.wikipedia.org/wiki/Bertrand_Meyer) que tem como princípio que todo método deve executar uma ação ou retornar dados, mas nunca os dois ao mesmo tempo. É importante frisar que como todo [design pattern](https://en.wikipedia.org/wiki/Design_pattern) ele não é uma bala de prata que vai resolver todos os nossos problemas e por isso iremos discutir um pouco sobre como e quando podemos aplicar esse padrão de desenvolvimento.

![Wanted bullet shot film gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ikm965rbk1b1uojmbdrm.gif)

## Definições
O autor definiu que todo método deve executar uma ação ou retornar dados, mas nunca os dois aos mesmo tempo. Nesse momento você pode estar pesando: "mas retornar dados não é uma ação?". Quando falamos em CQS **ações** ou **Commands** são operações que possuem [efeitos colaterais](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), ou seja, alteram o estado de uma variável, um registro no banco, etc... quando há alguma alteração envolvida estamos estamos executando uma **ação** ou **Command**. E quando estamos apenas consultando um dado de onde quer que seja sem causar efeitos colaterais no sistema estamos executando uma **consulta** ou **Query**.

## Casos de uso
É sempre bom lembrar que o uso de CQS é uma escolha de arquitetura e não quer dizer que realizar ação e retornar dados na mesma função seja errado. Existem os prós e contras quando utilizamos um determinado padrão e com CQS não é diferente. 
Um exemplo clássico e amplamente utilizado pela comunidade PHP que não faz uso do CQS é o [Eloquent ORM](https://laravel.com/docs/9.x/eloquent). Como podemos ver abaixo, quando criamos um objeto a classe nos retorna um instância contendo os dados do objeto criado.

```php
<?php
namespace App\Services;

use App\Domain\Users\UserTypes\BaseUser\Entities\UserEntity;

class UserService
{
    public function createEloquentUser(array $params = []): UserEntity
    {
        return UserEntity::create($params);
    }
}

```
O retorno dessa função é uma instância de `UserEntity`:

![Retorno da função createEloquentUser](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1fk5wb6tsku5wx9r1zlf.png)

Quando pensamos em principio da responsabilidade única, nossa  função **createEloquentUser** acaba tendo duas responsabilidades: **Criar o usuário** e **Retornar os dados do usuário** em projetos pequenos isso acaba não sendo um problema, mas, quando nosso projeto começa a crescer o CQS pode nos oferecer as seguintes vantagens:

- Performance: A maioria das aplicações executam mais leitura do que escrita então quando separamos as **Queries** e os **Commands** temos um ganho de perfomance, pois só executaremos essas ações onde é realmente necessário.

- Mantenabilidade: Como cada classe e método terão apenas uma a responsabilidade de ler com as **Queries** ou escrever com os **Commands** torna o código mais legível pois temos mais clareza do que está sendo executado apenas ao olhar o código.

- Fácil de testar: Como as responsabilidades das classes e métodos estão bem definidos acaba facilitando o processo de criação de testes unitários.

- Fácil de implementar: É fácil de implementar, pois basta lembrar que se é uma consulta criaremos uma classe **Query** se é ação que cria ou altera alguma parte do nosso sistema criaremos uma classe de **Command**. O exemplo abaixo mostra as classes necessárias para um [CRUD](https://developer.mozilla.org/pt-BR/docs/Glossary/CRUD) de usuários:

## Implementação

A criação, atualização e deleção de usuários são considerados ações, pois, elas causam efeitos colaterais ao alterar o estado de onde estão armazenamos os dados dos nossos usuários. Então, para esses casos devemos criar classes de **Command**:

- UserCreateCommand.php
```php
<?php
namespace App\Infrastructure\User\Commands;

use App\Infrastructure\Database\DatabaseORM;

class UserCreateCommand
{
    public function __invoke(array $creationParams = []): void
    {
        DatabaseORM::create($creationParams);
    }
}
```
- UserUpdateCommand.php
```php
<?php
namespace App\Infrastructure\User\Commands;

use App\Infrastructure\Database\DatabaseORM;

class UserUpdateCommand
{
    public function __invoke(int $userId, array $updateParams = []): void
    {
        DatabaseORM::update($userId, $creationParams);
    }
}
```

- UserDeleteCommand.php
```php
<?php
namespace App\Infrastructure\User\Commands;

use App\Infrastructure\Database\DatabaseORM;

class UserDeleteCommand
{
    public function __invoke(int $userId): void
    {
        DatabaseORM::delete($creationParams);
    }
}
```

Como nossas classes tem responsabilidades únicas e bem definidas então nesse caso optei por utilizar o método mágico [__invoke](https://www.php.net/manual/pt_BR/language.oop5.magic.php#object.invoke). Note também que nossas funções sempre retornam `void` pois utilizando CQS o **Command** deve apenas executar uma ação e nada além disso.

Agora, para retornar os dados que foram armazenados vamos criar nossas classes de **Query**:

- UserFindOneByIdQuery.php
```php
<?php
namespace App\Infrastructure\User\Queries;

use App\Infrastructure\Database\DatabaseORM;

class UserFindOneByIdQuery
{
    public function __invoke(int $userId): array
    {
        return DatabaseORM::findOne($userId);
    }
}
```

- UserFindByQuery.php
```php
<?php
namespace App\Infrastructure\User\Queries;

use App\Infrastructure\Database\DatabaseORM;

class UserFindByQuery
{
    public function __invoke(array $queryParams = []): array
    {
        return DatabaseORM::findBy($queryParams);
    }
}
```

Mas nem tudo são flores, como você pode ver pra executar uma tarefa simples de CRUD de usuários foi preço criar **5 classes** distintas e em um cenário com mais ações para cada outra ação ou consulta precisaríamos de mais classes, ou seja, apesar de termos um código mais limpo e organizado, temos um aumento na complexidade que nem sempre faz sentido em projetos pequenos. Por isso é importante ter em mente se as vantagens como ganho de perfomance e mantenabilidade a longo prazo realmente farão diferença invés de aplicar somente por estética.

## Conclusão

O Command Query Separation é um padrão de projeto que como qualquer outro tem seus prós e contras, ele nos ajuda a compreender melhor como separar as responsabilidades das nossas ações e consultas. E como os outros padrões deve ser utilizado com sabedoria em casos onde as vantagens compensam as suas desvantagens. Quando mais nos aprofundamos em padrões de projeto e arquitetura de software mais ferramentas teremos pra enfrentar os problemas do dia-a-dia e escolher a melhor ferramenta para cada problema.