RODRIGO BRANAS
20 de Junho de 2023

Um use case pode chamar o outro?

Esse talvez seja o grande dilema de quem começa no Clean Architecture, é a pergunta que todos sempre fazem e precisamos levar alguns aspectos em consideração antes de responder.

Normalmente essa necessidade vem da falta de um Domain Model, que no Clean Architecture é representado pela camada de entities.

Essa camada é responsável pelas regras de negócio independentes, que podem ser utilizadas e orquestradas pelos use cases para entregar o comportamento desejado pelos usuários.

As entities não são necessariamente objetos, podem ser meras funções, estruturas de dados, esse conceito não está associado necessariamente a um design orientado a objetos.


Qual é a diferença entre uma entity para um use case?

Imagine dois objetos Coordenada, com as propriedades latitude e longitude. É possível escrever um algoritmo que retorna a distância entre eles? Isso parece uma regra de negócio independente?

Com certeza! Essa regra de negócio não depende de nenhum recurso externo, é baseado apenas nos parâmetros de entrada e é possível criar um teste de unidade.

Qual use case poderia utilizar essa regra de negócio, por exemplo:

  • Ao fazer uma compra na Amazon é necessário calcular a distância entre o centro de distribuição e o comprador para calcular o frete
  • Quando solicitamos uma corrida no Uber precisamos calcular a distância entre a origem e o destino para estimar o preço
  • Para registrar a distância percorrida em um treino do Strava podemos utilizar o mesmo algoritmo

Repare que normalmente os usuários desses casos de uso não estão interessados nas regras de negócio independentes fornecidas pelas entities, ou alguém entra na Amazon pra calcular a distância entre dois pontos geográficos? No entanto, elas são fundamentais para a implementação dos use cases.

Quem está acostumado com um modelo anêmico, aquele que o Martin Fowler chama de Transaction Script, está condicionado a pensar de forma procedural, onde toda regra está espalhada em algo com o sufixo Service e os objetos são meras estruturas de transporte de dados, sem comportamento.

Não é uma questão de funcionar ou não, é só um design diferente, como todos, tem vantagens e desvantagens.

A desvantagem é não conseguir dividir bem a complexidade, acoplando regras independentes em recursos externos, prejudicando o reuso e dificultando os testes.


Não confunda as entities do Clean Architecture ou os objetos de domínio do Domain-Driven Design com as entities de um ORM, elas tem propósitos bem diferentes.

No ORM, uma entity faz um mapeamento entre um objeto e uma tabela do banco de dados e essa responsabilidade não deve ser misturada com as regras de negócio, não só porque elas mudam por motivos diferentes, quebrando o SRP, mas porque estão em camadas diferentes.

Voltando ao assunto, você já definiu uma camada de entities e ainda sente a necessidade de fazer um use case chamar o outro, caso contrário estará duplicando muito código?

Talvez seja necessário entender como os objetos de domínio estão sendo agrupados, é o que o Domain-Driven Design chama de aggregate e é um conceito pouco abordado no Clean Archicture.

A ideia não é ter entidades totalmente separadas umas das outras, isso naturalmente aumentaria a responsabilidade dos use cases.

Ao invés disso, os aggregates devem assumir esse papel, de Facade ou Façade (como o padrão do GoF), orquestrando a interação entre os objetos de domínio pertencentes à ele e com isso reduzindo o acoplamento entre os use cases e as entities.

Você já fez tudo isso, tem seus aggregates e repositories bem definidos e continua sentindo a necessidade de fazer um use case chamar o outro?

Eu sei exatamente onde estamos indo. Existem duas situações onde isso pode acontecer!

A primeira é quando existem casos de uso levemente diferentes, por exemplo:

  • Criar conteúdo
  • Copiar conteúdo
  • Importar conteúdo

Repare que todos eles de fato criam um conteúdo em um curso, a diferença é que copiar conteúdo criar um conteúdo a partir de dados de um conteúdo existente e importar conteúdo cria um conteúdo com base em um arquivo externo, provelmente uma planilha exportada de outro sistema.

Nesses casos é como se existisse um Decorator ao redor do caso de uso principal, imagine, é o mesmo comportamento, a diferença é que em copiar a origem é um conteúdo já existente, ou seja, é como se um use case incorporasse o outro. No caso do importar é a mesma coisa, a diferença é que vamos percorrer uma lista e chamar o use case de criar conteúdo tantas vezes quanto for necessário.

Não vejo problemas em fazer um use case chamar o outro nessas situações, simplesmente não faz sentido copiar a maior parte de um use case em outro apenas para mudar a forma como ele é utilizado.

No entanto, existem duas ressalvas. A primeira é, tome cuidado com a quebra do SRP, ou Single Resposibility Principle. Se você começar a mudar o use case principal por demandas introduzidas por outros use cases isso pode trazer muita fragilidade, quebrando os outros use cases dependentes, e complexidade adicional para o use case principal, por isso avalie se copiar e seguir caminhos diferentes não é a melhor opção.

A segunda ressalva tem relação com resiliência, estabilidade e performance. Imagina chamar o use case importar conteúdo com uma lista de 100 mil conteúdos. Pode não ser o caso, mas se for talvez seja melhor investir em padrões como um Command Handler, jogando as solicitações em uma fila e consumindo aos poucos, conforme a disponibilidade de recursos.


Utilize eventos de domínio

Por fim, e se esse outro use case fizesse parte de uma transação de longa duração, por exemplo, ao finalizar uma corrida é necessário processar o pagamento, depois emitir uma nota fiscal, enviar um email com o comprovante para o cliente, solicitar a avaliação da corrida e assim por diante.

Talvez esses use cases estejam até mesmo em bounded contexts diferentes e utilizar eventos vai proporcionar um acoplamento mais baixo, além de uma relação mais resiliente entre eles.

Por exemplo, caso o bounded context, que normalmente é um bom ponto de divisão de microservices, responsável pela comunicação esteja fora do ar, quando ele voltar vai processar as mensagens pendentes e enviar o comprovante ao cliente.

Isso contribui principalmente para aumentar a escalabilidade, independência e resiliência da sua arquitetura.


Conclusão

Nesse ponto você já entendeu que tem várias opções antes de fazer um use case simplesmente chamar o outro. A partir daqui, a decisão é sua.

Lembre-se que os autores escrevem livros que são reflexo do seu ponto de vista, das suas próprias experiências e não são leis ou regras que você está deixando de cumprir.

A sua realidade será diferente e a responsabilidade por cada decisão será sempre sua, então aprenda a colocar na balança os prós e contras de cada uma delas.

Quer fazer um use case chamar o outro, excelente, coloque na balança o que você ganha e o que você perde e como lidar com isso ao longo do tempo.


#cleanarchitecture #cleanarch #usecase