Introduzindo a camada de aplicação

Neste novo artigo sobre arquitetura de software, falarei sobre a camada de aplicação, suas principais características e alguns pontos a considerar para utilizá-la ou não.

Caso não tenha lido o <<artigo anterior sobre camadas>>, recomendo fortemente a leitura do mesmo para contextualizá-lo melhor.

O QUE É

A camada de aplicação (Application Layer) fornece um conjunto de serviços de aplicação (application services), os quais expressam as user stories (ou use cases) do software.

De modo simples, um serviço de aplicação recebe dados de seus clientes, como a interface de usuário, trata esses dados se necessário e chama um objeto do domínio para executar a operação de negócio.

Ele pode ainda fazer o controle de transação e tratar do controle de acesso do software, além de poder enviar notificações para outros softwares. Sendo assim, ele é um grande coordenador da operação de negócio a ser realizada pelo software e um cliente direto dos objetos de domínio.

(A camada de aplicação também é conhecida por camada de serviço (Service Layer), como mostrada no clássico livro Patterns of Enterprise Application Architecture do Martin Fowler.)

VISUALIZANDO NA ARQUITETURA CEBOLA

Abaixo segue como fica representada essa camada no modelo cebola:

Onion Architecture

Lembrando da regra de dependência entre camadas, a camada de aplicação conhece apenas a camada de domínio, sendo completamente ignorante a respeito de objetos e frameworks usados na camada mais externa. (Esse objetivo também pode ser atingido usando o Princípio de Inversão de Dependência, como mencionado no artigo anterior sobre camadas.)

UM PEQUENO EXEMPLO

Há várias formas de se implementar um serviço de aplicação, mas já dá para se ter uma noção de como ele é no código abaixo. Note que sequer estou tratando transações, por exemplo.

public class AtualizacaoDeEndereco
{
    private readonly IClienteRepositorio clienteRepositorio;

    public AtualizacaoDeEndereco(IClienteRepositorio clienteRepositorio)
    {
        this.clienteRepositorio = clienteRepositorio;
    }

    public void Executar(AtualizacaoDeEnderecoDto dto)
    {
        var cliente = this.clienteRepositorio.ObterPor(dto.IdDoCliente);
        var novoEndereco = new Endereco(dto.Rua, dto.Bairro, dto.Cep);
        cliente.AtualizarEndereco(novoEndereco);
    }
}

QUANDO USAR

Vamos a algumas perguntas:

– Existe mais de um cliente para uma determinada operação? Por ex.: duas interfaces gráficas ou uma interface gráfica e outro software.
– Existe apenas um cliente mas a operação é chamada em mais de um lugar, causando repetição de código?
– Existe alguma forma de integração entre uma operação e outros softwares, ou seja, parte da operação é realizada em outro contexto?

Se a resposta foi “sim” para pelo menos uma pergunta acima, é bem possível que a camada de aplicação seja uma boa ideia.

Agora vamos deixar de lado o “ter ou não ter” e focar na “intenção”. O que se espera com o software? Pretende-se mantê-lo vivo por muitos anos? Pretende-se facilitar sua manutenção caso haja troca de tecnologias de interfaces gráficas, bancos de dados ou outros serviços de suporte?

Novamente, se a resposta foi “sim” para as últimas perguntas, vale pensar com muito carinho no uso da camada de aplicação. Implementada de forma correta, junto com a camada de domínio, ela forma o núcleo do software, ou seja, tudo que ele se propõe a fazer, sem dependências de tecnologias e bibliotecas de terceiros.

Sendo assim, fica muito mais simples fazer a troca de toda a “periferia” com o surgimento de novas tecnologias.

QUANDO NÃO USAR

Implementar uma camada de aplicação (do jeito certo) pode ser algo trabalhoso, principalmente, se você nunca o fez. Exigirá um bom investimento.

Além disso, se você disse “não” a todas as perguntas da seção anterior, é possível que não valha a pena implementá-la. Pode ser que o software seja uma pequena aplicação com alguns CRUDs e nada mais (será? reflita…). Pode ser um mero “remendo” para uma solução maior a ser implantada e, portanto, será descartado em breve.

CONCLUSÃO

A camada de aplicação é uma boa forma de encapsular a lógica de aplicação, centralizando determinadas operações comuns, como o tratamento de transações.

Além disso, com o núcleo do software (aplicação e domínio) dependente apenas da plataforma da linguagem e nada mais (.Net, por exemplo), fica muito mais fácil evoluí-lo, já que o que mais muda ao longo do tempo são as tecnologias periféricas, como acesso a dados, interfaces gráficas, tecnologias de integração e serviços, etc…

É uma ótima opção para um software do qual se espera facilidade de extensão e longevidade.

30 comentários em “Introduzindo a camada de aplicação

Adicione o seu

  1. Ótimo artigo Robson! Por coincidência eu acabei de recomendar o uso dessa camada na solução de um cenário, vou recomendar a leitura deste.

    Gostaria de acrescentar que a camada Application está presente no DDD e que nela pode ser implementada a utilização do padrão Unit of Work (UoW) que permite que mais de uma entidade seja modificada num mesmo escopo, permitindo um rollback completo caso necessário.

    Abraços.

    Curtir

    1. Obrigado, Eduardo!

      É justamente do DDD que este nome se difundiu mais. Eu prefiro ele do que camada de “serviço”, já que “serviço” é um termo muito onipresente em nossa área.

      Sim, é uma boa ideia depender de uma abstração Unit of Work, isolando essa camada de detalhes de infra (sessão do NH ou contexto do EF, por ex) e preservando a regra de dependência das camadas (sempre de fora pra dentro).

      []s e valeu por comentar!

      Curtir

  2. Olá Robson.

    Mais uma vez, parabéns pelo artigo.

    Talvez a minha dúvida seja abordada em posts futuros, mas agradeceria se puder ao menos dar uma breve explicação.

    Em relação a UI, pelo que entendi a mesma não conhece a camada de Domínio e consequentemente não tem acesso às entidades que simbolizam os objetos do banco. Esse papel de “mapeamento” seria feito pela camada de DTO? A UI consumiria somente a Aplicação e a DTO? A Aplicação a DTO e o Domínio? A DAL só a Domínio?

    Ufa… (:

    Abs.

    Curtir

    1. Olá, Fabiano.

      Esse papel de “mapeamento” seria feito pela camada de DTO?
      Não sei se entendi muito bem. Voltando ao código mostrado no artigo, o DTO é recebido de parâmetro no método do serviço. O consumidor do serviço (um cliente ASP.NET MVC, por exemplo, é que se encarregaria de criar esse DTO com os dados necessários e chamar o serviço.
      Do DTO para um objeto de domínio, é mostrado no código, quando pego os dados do DTO e os uso na criação de um Endereco (<– objeto de domínio).
      Usar esse DTO como parâmetro é apenas uma das opções.

      UI consumiria somente a Aplicação e a DTO?
      Sim. Uma vez que controle transacional e de acesso estão encapsulados nos serviços de aplicação, toda operação é invocada por meio de um serviço de aplicação.

      A Aplicação a DTO e o Domínio?
      O DTO usado de parâmetro reside junto com o serviço na mesma camada. Sim, a aplicação é cliente direto do domínio.

      A DAL só a Domínio?
      Sim. A DAL (prefiro chamar de Infra.Persistencia) conhece o domínio quando ela precisa implementar repositórios cujas interfaces estão no domínio. Falei um pouco sobre isso no artigo sobre o Princípio de Inversão de Dependência.

      E sim, pretendo tratar de mais detalhes em posts futuros, inclusive, com mais exemplos práticos.
      Obrigado pelas questões!
      []s

      Curtir

  3. Ola Robson, tudo bem ?

    Trabalhando nessa Arquitetura Onion, eu teria o seguinte,

    Domain se comunica com a Service Application
    Domain se comunica com a Infrastrutura
    AutoMapper para conversao do Modelo de Visão para o Modelo de domínio.
    Composition Root , teria referência para Infrastrutura,Service Application ?

    Esta Correto, esqueci algo ?

    Abraços

    Parabéns

    Curtir

    1. E ai, cara!

      Na verdade é o contrário. Aplicação e Infra é que dependem do domínio.

      O AutoMapper é uma ótima ferramenta mas não é uma boa ideia usa-lo para mapear objetos em objetos do domínio. Um bom domain model preserva o encapsulamento (nada de setters abertos “de graça”) e é muito comum que NÃO possuam construtores sem parâmetros. Portanto, quando uso o Automapper é somente para mapear do domínio para view models.

      Sim, o composition root depende de muita gente (ou de tudo) pois ele é quem registra todas as dependências. No problem.

      É isso! Abraços!

      Curtir

      1. Olá Robson!

        Primeiramente muito obrigado pelos posts, está sendo de grande ajuda para eu que estou começando.

        Bom, você disse que utiliza o AutoMapper somente para mapear do Domain para a ViewModel, mas como fazer então para pegar os dados da ViewModel e passar para o Domain sendo que preciso de fazer esse mapeamento para, por exemplo, persistir através do Repository Pattern?

        Em relação à Arquitetura em Camadas, o AutoMapper deve ser configurado em um novo projeto na camada de CrossCutting a fim de não colocá-lo na User Interface, já que não é responsabilidade dela?

        Abraços!

        Curtir

      2. Olá, Raphael

        Se for um objeto existente, você irá obtê-lo do repositório com a informação vinda da viewmodel (como no exemplo dado no artigo).
        Se for um objeto novo, você irá cria-lo explicitamente, chamando seu construtor, um factory method ou uma factory específica para este fim:
        var novoCliente = new Cliente(viewModel.Nome, viewModel.CPF, ….. ). Isso porque, como dito antes, se seu domínio é OO, você deve preservar o encapsulamento e garantir as regras de criação do objeto.

        Qto ao mapeamento para a view, se você possui uma camada de aplicação, essa responsabilidade ficaria nela (o serv de aplicação obtem os dados do repositório e chama um mapeador para retornar o dto (viewmodel) apropriado para a tela consumir. Senão, ficaria na sua camada de apresentação mesmo, sem problemas.

        Espero ter respondido!
        []s

        Curtir

  4. Robson,

    Muito bacana esse post cara!! Aliás, parabéns por todos os demais posts também !

    Acho que uma dúvida prática que sempre recai sobre esse assunto (comigo quase sempre) é relação às classes que representam as entidades. Um amigo falou um pouco sobre isso num comentário anterior.

    Vamos supor que eu esteja utilizando Entity na minha camada de acesso a dados e no outro extremo eu tenho um Asp.Net MVC da vida. Na minha camada dados eu preciso de uma entidade Cliente. Na minha camada de apresentação eu também preciso dessa classe. Vamos ser simples e supor que meu interesse seja retornar dados em jSon e é importante manter a estrutura do meu objeto. Como isso pode ser feito de forma correta? Não posso fazer minha camada de UI conhecer minha camada de dados, onde estão todas as entidade que eu preciso. Duplicar os objetos em outra camada e fazer a.propriedade = b.propriedade me parece bizarro.

    Pode me dar uma dica de como resolver esse problema? Pode ser falta de conhecimento meu, mas DTO não me deixa confortável.

    Muito obrigado e mais uma vez, parabéns!

    Curtir

  5. Grande Robson, excelente artigo.

    A dúvida que tenho é simples. Nos cenários DDD que tenho estudado sempre indicam que o Domínio deve conter as entidades, as interfaces de repositórios e as interfaces de serviços.
    A responsabilidade de implementar os repositórios é da Infra.Persistencia.

    Já a responsabilidade de implementar os serviços, seria do próprio domínio para posteriormente serem invocados pela camada de Aplicação? Ou a implementação dos serviços de domínio devem ser realizados pela camada de Aplicação?

    Curtir

    1. Ola, Lano
      Obrigado!

      Default: serviços de domínio são implementados na camada do domínio.

      Em casos específicos, pode ser que o problema que ele precisa resolver necessite de outra aplicação. Neste caso, faz sentido manter apenas uma interface na camada de dominio e implementa-lo na camada de INFRA, uma vez que a implementação faz chamada a outra aplicaçao (por ex, consumindo um servico REST).

      Espero ter esclarecido.
      []s

      Curtir

      1. Ok, a relação de domínio e infra está bem clara. Mas qual o objetivo da camada de aplicação?

        1 – As Controllers representam esta camada?

        2 – Ou é necessário ter uma class library que apenas reflita os serviços do domínio?

        Outra questão que me confunde é, quais são as operações implementadas no repositório que não tem necessidade de passar pelo serviço de domínio, ou até mesmo pela camada de aplicação, considerando o item 2.

        Curtir

      2. 1. Não. Controllers estão uma camada mais externa (presentation). Eles recebem requisições e despacham o comando para um serviço de aplicação.

        2. Aí é uma questão de mera organização. A principio não. Serviços de dominio, juntamente com entidades, value objects, domain events, repositorios (interfaces) podem ficar na mesma class library, afinal todos representam conceitos de negócio. Só recomendo organiza-los por conceitos de dominio e NÃO separa-los em pastas por tipo (uma pasta so pra Entidades, por ex).

        Que tipos de operações? Repositórios representam uma coleção de determinada entidade raiz, portanto eles devem ter metodos simples para adicionar e remover (se for necessário) uma entidade da coleção e métodos que sirvam para obter um ou determinadas entidades da coleção. Também depende da sua arquitetura. Voce pode optar por passar sempre pela camada de aplicação (ou seja, UI nao pode dar um bypass nela e falar direto com o domínio) e ela ficaria responsavel por prover comandos e consultas para as camadas mais externas. Apesar de mais restritivo e pode exigir mais mapeamento de tipos de uma camada pra outra, você pode ganhar em outros pontos, como controle de acesso e transação, se seus serviços de aplicação cuidarem dessas questões.

        []s

        Curtir

  6. Agradeço pelos esclarecimentos.

    Segundo o que você explicou, não posso invocar um método do repositório diretamente pela UI, mas poderia invocar o método exposto no serviço do domínio sem passar pela aplicação?

    Qual a diferença entre o serviço de domínio e o serviço de aplicação? Seria apenas o controle transacional? Dado o cenário que você tenha que exibir na tela uma lista de todos os clientes usando o seguinte caminho:

    Controller.Index() > ClienteAppService.ListarTodos() > ClienteService.ListarTodos() > ClienteRepository.GetAll()

    Não seria extremamente burocrático, já que estou apenas refletindo as operações de uma camada para outra?

    Quanto á separação de pastas, eu realmente estava fazendo por tipo (Entidades, Objetos de Valor, Servicos, etc), mas por orientação de um artigo do Eduardo Pires .rsssss

    Desculpa insistir demais neste assunto, mas tem sido muito importante discutir estes cenários antes de encararmos nossos projetos com esta arquitetura.

    Curtir

    1. Olá, Lano

      Um serviço de domínio executa alguma operação de negócio, alguma operação que não pertence exclusivamente a alguma entidade.

      Um serviço de aplicação, como mostrado no post, executa alguma operação da aplicação, tratando dados de entrada e chamando objetos do dominio, por exemplo. Ele tb cuida de ‘transação’, ‘controle de acesso’ e pode controlar o progresso de alguma operação demorada, por ex. Serviço de aplicação NÃO tem lógica de negócio.

      Qto ao exemplo que vc deu, existe uma etapa desnecessária. “ClienteService.ListarTodos()” não faz sentido. “ListarTodos” é, claramente, uma função do repositório (ok, “todos” pode ser perigoso mas deixemos isso por hora). Sendo assim, essa operação pode ser excluída. Você não precisa de um serviço de domínio apenas para delegar operações para o repositório. Isso sim, concordo, é burocrático além de conceitualmente incorreto, como tentei explicar nas frases anteriores.

      Sendo assim, seriam 3 etapas no caminho e não 4. Um serviço de aplicação normalmente será bem enxuto, com métodos de pouquísimas linhas, normalmente usando objetos de domínio, como criar uma entidade e adiciona-la ao repositório, mas lembre-se que ele cuida da parte transacional, por ex.

      Se você optar por não criar uma camada de aplicação, responsabilidades extras acabarão vindo para a interface gráfica (como as já mencionadas, controle de acesso e transacional), o que pode não ser um grande problema se o software for pequeno e essas responsabilidades estiverem bem implementadas. Eu tenho considerado fortemente o uso da camada de aplicação, deixando o software mais preparado para o futuro sem maiores impactos.

      Espero ter ajudado. Qto a ‘insistir’ não tem motivo pra se desculpar. Comentários e questões acabam enriquecendo muito mais o conteúdo do post, sendo assim, eu é que agradeço.
      []s

      Curtir

  7. Robson, primeiramente parabéns pelo artigo, ele foi bastante esclarecedor, mas aproveitando a dúvida do Lano, ainda me resta entender exatamente essa questão entre serviços de dominio e aplicação.

    Acima você respondeu que seria desnecessário o 3° passo onde a aplicação consumiria o método ClienteService.ListarTodos(), que por sua vez iria acionar o repositório, sendo assim, o serviço da aplicação chamaria diretamente o repositório, ficando:

    Controller.Index() > ClienteAppService.ListarTodos() > > ClienteRepository.GetAll()

    A dúvida que fica é o que eu teria em cada uma dessas camadas de serviços ? Você poderia mostrar algum exemplo prático da diferença e responsabilidade de ambas?

    Quando você disse que o serviço da aplicação não tem lógica de negócio, eu fiquei imaginando que essa camada seria uma ponte entre UI e Data (repositórios), seria isso?

    Em termos de dependências, como que ficariam? Tanto o serviço da aplicação como do domínio possuem acesso a camada de dados? A camada de apresentação tem acesso somente ao serviço da aplicação, domínio ou ambas?

    Obrigado e desculpa caso tenham levantado novamente essa dúvida.

    Curtir

    1. Olá, James.
      Desculpe pela demora.

      Vamos lá!
      Na verdade, a ÚNICA camada de serviços do exemplo é a camada de aplicação (eu prefiro este nome pq ‘serviços’ é um termo extremamente sobrecarregado).

      Controller.Index() -> camada de apresentação. Controllers, filters, qualquer código que faça uso de ‘coisas’ do framework específico (por ex.: ModelState, Session, Cookies etc..)

      ClienteAppService.ListarTodos() -> camada de aplicação. Seria uma ponte entre UI e dominio. Veja o desenho da cebola do post. A dependencia entre uma camada e outra é de fora pra dentro. Serviços de aplicação usam diretamente o dominio (como mostrado no exemplo, IClienteRepositorio e Endereco são objetos do domínio). Eles tambem podem controlar acesso, transacao, interpretar entrada em outros formatos (XML, JSon) e notificar por email. Ou seja, eles são serviços de coordenação da aplicação, portanto sua implementação é bem pequena, somente chamando colaboradores para entregar a funcionalidade completa.

      “Tanto o serviço da aplicação como do domínio possuem acesso a camada de dados?” Indiretamente. Ambos usam somente as interfaces dos repositórios. Eles não possuem referencia à camada de dados (acompanhe minha série sobre Dependency Injection, que pode ajudar a esclarecer).

      “A camada de apresentação tem acesso somente ao serviço da aplicação, domínio ou ambas?” Nada impede. Você precisa estabelecer uma regra e se manter consistente com ela. Vai depender da complexidade da aplicação, do que se quer com ela ao longo do tempo. Isolar totalmente o domínio da UI vai ter um trabalho extra, uma complexidade adicional que, talvez pelo tamanho da aplicação, nem faça sentido.

      Vou escrever em breve um novo post com um serviço de aplicação mais ‘encorpado’. Tbem pretendo jogar no github um exemplo de código usando a arquitetura cebola com DI. Espero não demorar muito.
      []s

      Curtir

  8. Olá Robson,

    Fiquei com dúvida em relação a este trecho: “Ele pode ainda fazer o controle de transação e tratar do controle de acesso do software”.

    Com base na Clean Arch, penso que não deveria existir dependência externa dentro da camada de aplicação, como seria este controle de transação? Entendi que seria algo relacionado ao ORM, ou estou enganado?

    Curtir

Deixar mensagem para jeffersonsnascimento Cancelar resposta

Blog no WordPress.com.

Acima ↑