Quando começamos a trabalhar com a camada de aplicação, popularizada nos últimos anos pelo Domain-Driven Design, ficam dúvidas sobre sua verdadeira função e sobre qual código devemos colocar nela. Escrevi dois artigos anteriormente que ajudam a elucidar essas questões aqui e aqui.

Neste terceiro, mostro uma divisão mais clara entre lógica de negócio e lógica de aplicação e algumas dicas para não confundirmos as duas.

LÓGICA DE NEGÓCIO (BUSINESS/DOMAIN LOGIC)

Compreende todo o código que lida com regras de negócio, ou seja, código que diz o que determinada área de negócio faz. Por exemplo, em uma área que trata de assinaturas de revistas, aplicar uma determinada política de desconto nas assinaturas é uma regra de negócio.

Toda lógica de negócio deve residir na camada de domínio (business/domain layer), distribuída entre diversos tipos de objetos como entidades, value objects, (domain) services, entre outros.

Lógica de negócio NÃO envolve preocupações técnicas, como acesso a dados, exportação de dados em formato de arquivos, notificações, etc. Ela sequer sabe em qual tipo de aplicação ela está sendo usada (mobile, desktop, web, sendo exposta via REST?).

LÓGICA DE APLICAÇÃO (APPLICATION LOGIC)

Tomando o exemplo anterior, para concluirmos uma assinatura de uma revista, além da lógica de negócio envolvida, existe uma série de outras preocupações que devem ser atendidas para completarmos essa “user story / use case”, como, por exemplo, cuidar do controle transacional e fazer o log da operação. Portanto, a lógica de aplicação é quem coordena todas essas operações, garantido que a user story “assinar uma revista” seja atendida com sucesso.

Toda lógica de aplicação reside na camada de aplicação (application layer), distribuída em serviços de aplicação, cada um deles com uma responsabilidade bem definida, que significa atender determinado use case.

ONDE VAI O CÓDIGO? ALGUMAS DICAS…

Dito tudo isso, fica fácil saber o que é e onde vai código de negócio e código da aplicação. No entanto, vale reforçar com algumas dicas:

1. Lógica de aplicação coordena as operações e não as implementa, ou seja, um serviço de aplicação simplesmente faz uso de lógica de domínio e demais serviços técnicos. Portanto, código que lida com logging, por exemplo, ainda é código de infraestrutura.

2. Observe se os serviços de aplicação possuem condicionais e loops. Isso pode ser um smell de que há código ali que deveria estar encapsulado em alguma classe de negócio ou de infraestrutura.

3. Observe como se dá a interação entre os objetos de domínio. O mais simples possível é que um serviço de aplicação interaja com um objeto de domínio e com seu repositório para obtê-lo/persisti-lo:

// método de um serviço de aplicação responsável
// pelo cancelamento de pedidos
// .......
var pedido = this.repositorioDePedidos.ObterPor(idDoPedido);
pedido.Cancelar();
this.repositorioDePedidos.Salvar(pedido);

4. Caso haja uma sequencia de interações entre vários objetos de domínio, verifique se essas operações representam um conceito do domínio, isto é, se aparecem sempre juntas. Em caso afirmativo, encapsule-as em um serviço de domínio. Por exemplo, se sua lógica de aplicação coordena o crédito em uma conta bancária, o débito de outra conta e a geração de movimentos de crédito e débito no extrato, esse conjunto de operações poderia ser encapsulado em um serviço de domínio responsável por uma “transferência bancária”.

5. Podem haver casos onde uma sequencia de interações entre objetos de domínio não forma obrigatoriamente um conceito único, ou seja, em um determinado serviço de aplicação, ela aparece na íntegra, enquanto em outro serviço, apenas parte dela é necessária. Vejamos um exemplo:

// método de um serviço de aplicação
// ........
var pedido = new Pedido();
this.repositorioDePedidos.Adicionar(pedido);
// ........
politicaDeDescontos.Aplicar(cliente, pedido);

// ........

No exemplo acima, o serviço de aplicação implementa um use case onde um novo pedido é criado e imediatamente já é aplicado um desconto a ele. Suponha agora que tenhamos outros use cases, onde usuários com permissões diferentes em momentos diferentes, podem criar pedidos sem aplicar nenhum desconto e podem aplicar descontos em pedidos já existentes. Cada combinação dessas ficaria em um serviço de aplicação diferente (*).

(*) Esta última dica é uma sugestão de Scott Millett, em seu livro “Patterns, Principles and Practices of Domain-Driven Design”.

LÓGICA DE APLICAÇÃO FORA DOS SERVIÇOS DE APLICAÇÃO

Em alguns softwares que não possuem uma camada de aplicação, a lógica de aplicação costuma ficar em uma camada mais externa. Por exemplo, em uma aplicação ASP.NET MVC, essa lógica reside nas actions dos controllers. Para evitar repetição, em geral, a lógica de aplicação acaba ficando distribuída, parte nas actions e parte em recursos do framework utilizado (no caso do ASP.NET MVC, é comum o uso de filters para lidar com preocupações técnicas, como tratamento de exceções, logging, autorização, entre outros).

Embora a solução acima seja mais simples e até mais divulgada, eu recomendo fortemente concentrar essas operações em serviços de aplicação, principalmente em softwares mais complexos e que pretende-se manter ao longo dos anos. (Indico a leitura dos artigos citados no início deste.)

CONCLUSÃO

Lógica de negócio/domínio e lógica de aplicação são tipos diferentes de lógica. Saber diferenciá-las e onde colocá-las no software torna o código mais legível, mais coerente com o negócio e com o modo pelo qual o usuário opera o software, mais coeso e fácil de testar.

Para saber mais sobre o assunto, indico os livros de Domain-Driven Design:

Domain-Driven Design: Tackling Complexity in the Heart of Software
Implementing Domain-Driven Design
Patterns, Principles, and Practices of Domain-Driven Design