Dúvida na comunicação entre camada de aplicação e camada de negócios

Boa tarde!

Há alguns anos atrás quando comecei a estudar e aprender desenvolvimento (Java na época) me cadastrei aqui no fórum. Logo depois comecei a trabalhar com .NET e acabei esquecendo deste fórum. Nós últimos tempos, estou procurando me aprofundar em Design e Padrões de Projeto e acabei caindo aqui novamente. Desde então (já uns 4 meses) frequento diariamente apenas como leitor, mas agora chegou a hora de postar umas dúvidas e pedir a ajuda dos colegas de fórum :slight_smile:

Como falei, trabalho com .NET e procuro aplicar o máximo possível as boas práticas de projeto. Estou estudando DDD (já li o “resumo” do livro do Eric Evans do InfoQ, diversos blogs de pessoas que postam aqui no fórum e blogs indicados por eles, li bastante coisa de Fowler e estou estudando atualmente um pouco o PEAA entre outras literuaturas menores) e tenho uma dúvida que ainda não consegui achar uma resposta satisfatória.

Em .NET, não utilizamos MVC como em Java (ainda, pois ASP.NET MVC está saindo) então não temos os conceitos de controllers tão explicitos quando vocês, portanto algumas abordagens devem ser levemente adaptadas. Meu cenário é:

Modelei (estou tentando) uma pequena parte de um sisteminha de E Commerce (estou colocando diagrama anexo) utilizando Entidades (Atributos + Comportamento) e Repositorios. Desta maneira, já consegui tornar meu código bastante testável e acredito estar no caminho certo para um design mais clean e mais fácil de manter e testar.

Exemplo de teste:

        /// <summary>
        ///A test for Total
        ///</summary>
        [TestMethod()]
        public void TotalTest()
        {
            IPedidoRepository pedidoRep = mock.DynamicMock<IPedidoRepository>(null);
            IProdutoRepository produtoRep = mock.DynamicMock<IProdutoRepository>(null);

            using (mock.Record())
            {
                Expect.Call(pedidoRep.CriarPedido()).Return(new Pedido());
                Expect.Call(produtoRep.CriarProduto()).Return(new Produto()).Repeat.Twice();
            }

            Pedido target = pedidoRep.CriarPedido();
            
            Produto prodBebida = produtoRep.CriarProduto();
            prodBebida.Preco = 100;
            
            target.AdicionarProduto(prodBebida, 1);

            Produto prodComida = produtoRep.CriarProduto();
            prodComida.Preco = 35.20m;
                        
            target.AdicionarProduto(prodComida, 7);

            decimal expected = 346.40m; 
            decimal actual = target.Total;

            Assert.AreEqual(expected, actual);
        }

Tenho no modelo minhas entidades: Pedido, ItemPedido (value object), Cliente, etc.
Meus repositórios (o conceito) são definidos por Interfaces implementadas por DAO, visto que são persistências simples inicialmente (apenas CRUD).
Factories das Entidades que coloquei junto com o Repositórios (método CriarPedido() por exemplo) visto que também são simples e não necessitam de uma classe apenas para isso.

Como em .NET trabalhamos com WebForms e temos acesso aos controles da página através do code-behind não se faz necessário popular minhas entidades a partir de um objeto request passado para uma action, mas sim no próprio code-behind através de:

Objeto.Propriedade = txtQualquer.Text;

No início me pareceu um pouco estranho criar entidades e efetuar chamadas ao repositório diretamente do code-behind (que fica na camada de aplicação), mas dado o cenário simples achei que iria complicar muito se fizesse diferente.

Exemplo de persistência CRUD de um Cliente:

    protected void btnSalvar_Click(object sender, EventArgs e)
    {
        if (ClienteAtual.Id.Equals(0))
            Adicionar();
        else
            Atualizar();
    }

    private void Atualizar()
    {
        //ClienteAtual é mantida em viewstate na minha camada de aplicação

        IClienteRepository rep = RepositoryFactory<IClienteRepository>.Create();        
        ClienteAtual.Nome = txtNome.Text;
        ClienteAtual.CPF = txtCPF.Text;
        ClienteAtual.Email = txtEmail.Text;
        rep.Atualizar(ClienteAtual);

        Response.Write("Cliente atualizado com sucesso!");
    }

    private void Adicionar()
    {
        IClienteRepository rep = RepositoryFactory<IClienteRepository>.Create();
        Cliente novoCliente = rep.CriarCliente();
        novoCliente.Nome = txtNome.Text;
        novoCliente.CPF = txtCPF.Text;
        novoCliente.Email = txtEmail.Text;
        rep.Adicionar(novoCliente);

        Response.Write("Cliente inserido com sucesso!");
    }

No entanto, nesse momento estou pensando em implementar situações que não sejam CRUD, como efetuar um pedido por exemplo. Neste caso, mais de um objeto estaria envolvido na transação então não acredito que seja bacana fazer esse processo diretamente no code-behind. Além é claro, de ser menos bacana ainda colocar transações na camada de aplicação. Sendo assim, criei uma Service para Pedido, pois o processo de efetuar o pedido, no meu ponto de vista, não é responsabilidade do Cliente e nem do Pedido. Seria de um “funcionário” da loja, mas a título de simplicidade bastou criar o PedidoService.EfetuarPedido().

Agora as dúvidas: como tratar a criação das entidades nesse caso? Crio elas na minha camada de aplicação (code-behind), passo para o PedidoService para o processamento e posterior chamada ao repositório para atualização de dados ou PedidoService apenas modifica meus objetos e a própria camada de aplicação chama o repositório para persistência (como no exemplo do cliente ali em cima)? Minha classe PedidoService pode acessar meus repositórios? Acredito que sim, pois todos são objetos de domínio que de acordo com o que entendi tem livre acesso entre si. É isso mesmo?

Acredito que a minha principal dificuldade neste momento é tratar bem a separação das camadas de Aplicação e Domínio. Pensei em utilizar Façade, mas nesse caso o que eu retornaria para a camada de aplicação? O que a camada de aplicação iria passar para o Façade?

Desculpem o post grande e qualquer bobagem que eu tenha dito a respeito de Java.

Agradeço desde já qualquer ajuda :slight_smile:

Olá,

Não entendi muito bem o que é sua camada de aplicação… pelo exemplo que você deu, está parecendo que vc tá chamando a camada web de camada de aplicação.

Eu desenvolvo o modelo de domínio e uso a camada de aplicação como uma fachada pra ele… na camada de aplicação controlo transações e segurança, e essa camada da uma noção do que o sistema é capaz de fazer por use case/user history.

Essa camada funciona como uma orquestradora e acessa repositórios e os objetos do domínio.

Quem acessa a camada de aplicação neste caso é a camada de visualização. Eu tambem utilizo meus objetos do dominio na camada de visualização, para que os frameworks mvc preeencham os objetos de domínio e então a camada de view envia o objeto de domínio preenchido para a camada de aplicação.

espero ter sido claro.

[]s
Ferry

Opa!

Como no momento não existe uma camada de aplicação explicita, mas somente camada Web (aspx e code-behind) e camada de Domínio minha camada Web acabando atuando também como uma camada intermediaria de aplicação.

Acredito que entendi seu exemplo, mas ainda restam algumas dúvidas quanto a ele: você utiliza sua fachada em todos os casos? Mesmo quando existem operações muito simples? Imaginando que sim, qual critério você utiliza para gerar suas classes de fachada? Você cria uma por use case/user history? Teriamos então o código abaixo?

ClienteFachada.AdicionarCliente(Cliente novoCliente) 

Existem apenas dois pontos ainda nesse cenário que você apresentou com os quais eu não me sinto muito confortável ainda. São eles:

  1. Se eu utilizar essa fachada sempre, inclusive em casos bem simples (como meu exemplo acima), vou acabar gerando muita duplicidade de código visto que a fachada irá apenas fazer uma rechamada
  2. Ainda acho estranho a camada Web invocar meus Repositorios/Factories para criação de entidades

Muito obrigado pela ajuda! :smiley:

Opa

A quantidade de fachadas é variável, se você está ficando com fachadas muito pequenas pode agrupar… eu prefiro deixar uma fachada por caso de uso/user history mesmo, pois acho que fica mais fácil encontrar os fluxos que o sistema executa… mas isso não é uma regra.

Em relação a nomenclatura, eu não usaria o nome ClienteFachada, usaria algo com IncluirClienteFacade, pois fica mais explicito qual history/caso de uso aquela fachada orquestra.

Eu utilizo fachadas para os casos que precisam de transação… se você tem uma entidade cliente na web por exemplo e quer pegar todos suas compras efetuadas no mês X, você não precisa de uma fachada, pois vc tem o objeto na mão e este método não altera o estado do sistema… Internamente ele pode utilizar o repositório e disparar uma consulta, mas como eu disse, não altera o estado, então não tem problema.

Duplicação de código não acontece, mas em alguns casos pode acontecer da fachada executar somente uma chamada da camada de domínio… Eu usaria a fachada mesmo assim, pois se os requisitos mudarem e a estoria mudar um pouco, vc sabe o ponto exato onde alterar.

Eu não costumo deixar a camada web acessar os repositórios diretamente (a nao ser que seja dentro de alguma entidade. e somente para consultas);

Não vejo problema em utilizar factories do domínio na camada web…

bom é isso.

[]s

Hmmmm…!

Vamos ver se eu entendi então:

  1. Você cria fachadas de acordo com use case/user story então teria a IncluirClienteFacade com o método Incluir por exemplo. Certo? Tratando-se da inclusão do cliente, eu não consigo pensar em nenhum outro método que essa classe fachada teria. Então ficaria com apenas um metódo mesmo?

  2. Uma tela que Inclua/Altere/Exclua uma entidade utilizaria três fachadas. Certo?

  3. No caso de uma operação que não implique em mudança de estado, você acessar os objetos do dominio diretamente pela camada Web. Certo?

  4. Tendo os métodos de factory de entidades nos meus repositórios eu teria também que acessa-los na camada Web para poder criar as entidades. Certo?

De uma forma geral, eu concordo bastante com esta maneira. O único bad smell que eu ainda sinto é o fato de a camada Web acessar a camada de domínio diretamente e não através da camada de Fachada. Ta certo que isso aconteceria somente em alguns casos, mas o ideal não seria sempre acessar de forma transparente através da fachada?

Obrigado novamente!

Opa,

Não necessariamente. Na verdade nestes casos eu deixaria tudo na mesma fachada… eu gosto de amarrar com caso de uso mas isso é gosto pessoal… bastante gente não gosta. E todas essas operações poderiam ser um unico caso de uso por exemplo, com o nome Manter [Entidade].

Não acredito que acessar os objetos de negócio da camada web sejam um BadSmell… afinal de contas sua camada de visualização depende do negócio…

Seria mesmo responsabilidade do repositório instanciar entidades? Eu nao gosto desta abordagem.

[]s

Opa!

Neste caso então a fachada atuaria mais próximo de um Service. Minha fachada estaria horizontalmente ligada com as entidades e os repositórios e não acima deles atuando como um intermediador. Certo? Inicialmente eu havia imaginado definir um objeto apenas para isolar a comunicacao da camada web com o modelo de domínio e nele eu colocaria as informações pertinentes aos use cases/user story. Mas nesse caso eu estaria criando um cara a mais pra enxer o saco no futuro. Enfim, estou tendendo mais a utilizar a sua abordagem e deixar esse isolamento completo que eu queria de lado.

Hmm… por conceito acredito que cada agregado deveria ter sua factory única e independente, mas na prática não tenho objetos tão complexos pra justificar criar uma classe para cuidar de cada um dos meus agregados. Geralmente pra inicializar meus objetos em estado válido bastar atribuir valores padrão para propriedades do tipo Datetime e cuidar para que não existam propriedades NULL.

Sendo assim eu deixo a factory nos repositórios. Meu problema nesse caso de interpretar a melhor forma de fazer é a granularidade. É correto ter uma classe + generics como factory ao invés de manter nos repositórios?

Obrigado!

Nenhuma outra opinião?

Dei uma pesquisada aqui no GUJ e não achei nenhum tópico exatamente nesses moldes.

Agradeço muito a ajuda do Ferryman, mas se possível, eu gostaria de levar pro nosso lado (.NET) mais algumas opiniões :smiley:

Valeuzão!