|
01 de outubro de 2010, por Denis Ferrari em Gerais Após a palestra na qual apresentei o conceito de persistência plugável, recebi alguns pedidos para construir uma demonstração da substituição do Entity Framework 4 pelo NHibernate. É importante ressaltar que esse procedimento é possível graças à junção de várias técnicas já conhecidas, ou seja, não estamos criando nada, apenas montando uma nova receita com ingredientes já conhecidos. Outro ponto que gostaria de ressaltar é que os conceitos aqui apresentados podem (e devem) ser aplicados em vários aspectos de um projeto, não só na camada de persistência, mas em qualquer camada que dependa de uma certa tecnologia e que precise passar a trabalhar de forma independente para que o software seja preservado..
Vamos começar analisando as camadas da solução: Camadas
Domínio: Camada responsável por armazenar as classes que representam o domínio do problema. A camada de domínio é o coração do projeto. É altamente recomendável que ela seja isolada do restante do projeto. Infra: Responsável por fornecer os recursos comuns a todas as camadas do projeto. Através de injeção de dependência, essa camada pode fornecer qualquer tecnologia necessária para problemas pontuais, como por exemplo: Envio de e-mail, Cliente do Twitter, etc. Persistência: Camada responsável por persistir os objetos criados no sistema. Essa camada pode ser construída com qualquer tecnologia de acesso a dados, desde ADO.NET puro até um ORM como EF4 ou NHibernate. Com as responsabilidades definidas para cada uma das camadas, podemos iniciar a construção da camada de domínio. Domínio
Como nosso foco não é resolver um domínio complexo e sim provar o conceito, vamos utilizar um domínio simples, representado somente pela classe “Link” e seu repositório. Seguem os códigos das classes do domínio:
Para criar a independência da implementação concreta de um ORM, precisamos trabalhar em um nível mais alto. Ainda no domínio, vamos definir o conceito de Unidade de Trabalho, e implementá-lo de acordo com cada ORM (Assim como os repositórios).
O “golpe do século” dessa solução está na simples abstração dos conceitos de Unidade de Trabalho e Repositório. Notem que o domínio não conhece suas implementações concretas, mas sim seus contratos (interfaces). Quando precisarmos das implementações concretas, pedimos à camada de Infra. Para saber mais sobre os Patterns Unit Of Work e Repository, veja as descrições no site do Martin Fowler: Unit Of Work: http://martinfowler.com/eaaCatalog/unitOfWork.html Com o domínio definido e implementado, vamos partir para construção do banco e das camadas de persistência. O Banco de dados
Camada de persistência com Entity Framework 4 Agora que já temos nosso domínio montado com as abstrações necessárias, vamos construir as implementações das interfaces Unidade de Trabalho e Repositório. Nossa camada de persistência conhece camada de domínio, pois precisa das mesmas para criar os mapeamentos e conhecer os contratos (interfaces) que precisar cumprir. A implementação da Unidade de Trabalho consiste em manipular o principal objeto no EF, o Contexto. A solução é fazer com que a Unidade de Trabalho possua um contexto e consiga repassar o mesmo para as implementações dos repositórios.
Como o repositório depende da Unidade de Trabalho, o mesmo terá acesso (através de um cast) ao Contexto, e assim, poderá usufruir de todos os recursos dele.
A criação do Contexto não foi abordada, pois não foge do padrão de trabalho com classes POCO. De qualquer forma, é possível fazer download do código fonte desse projeto ao final do artigo. Agora que já possuímos o domínio e a persistência com base no Entity Framework, vamos montar nossa camada de Infra e um código de exemplo no qual poderemos ver a integração dos componentes acontecer sem acoplar o código de negócio à tecnologia utilizada. A camada Infra O primeiro passo é construir, ainda na camada de persistência do EF4, um módulo do Ninject que irá mapear para nossa camada de Infra qual classe irá atender a determinada interface (ou fornecedor irá atender a determinado contrato).
Com o módulo definido, precisamos de uma classe que seja responsável por resolver nossas interfaces. Essa classe irá utilizar o Ninject internamente para isso, mas o acesso será somente através dos nossos métodos.
Com a nossa Fábrica pronta, podemos construir um código de exemplo para junção de tudo que foi construído até agora. Utilizamos um projeto de teste para demonstrar a utilização do código:
Como podemos ver, não usamos diretamente nenhuma classe da camada de persistência. A responsabilidade de obter as instâncias necessárias é da nossa Fábrica, ou seja, abstraímos as implementações concretas e trabalhamos somente com os membros declarados nas interfaces. E como ficaria esse mesmo código com NHibernate? O NHibernate Para substituir o EF4 pelo NHibernate, precisamos apenas construir as implementações das interfaces IUnidadeTrabalho e IRepositorioLinks. Vejamos:
A unidade de trabalho do NHibernate ao invés de manipular o Contexto, irá manipular a Sessão. A lógica de construção do repositório também é muito semelhante.
Com as duas classes principais construídas, precisamos informar à fábrica que a mesma irá trabalhar com o NHibernate e não com o EF4. Para isso, vamos construir mais um módulo do Ninject e alterar apenas uma linha de código na Fábrica.
Quando rodarmos o nosso código de teste, vamos perceber que as classes do NHibernate estão sendo utilizadas, e não as classes do EF4. Como vimos, manter um certo nível de abstração (nem tanto e nem tão pouco) facilita muito nos momentos em que o software sofre uma alteração. Esse artigo não mostra uma alteração que vemos todos os dias, mas mostra que qualquer alteração (inclusive a demostrada aqui) pode ser muito mais suave se durante a construção abstraírmos os elementos certos. Download do projeto Abraços!
Comentários:
23 Comentários postados em "Persistência plugável"
Antonio Jr on outubro 1st, 2010 at 22:03 #
Denis, Eu fui um dos que pediu esse post. Ficou muito legal.
Tweets that mention Persistência Pugável - De longe o maior post que eu já fiz no #SouDev #EF4 #NHibernate #DotNet -- Topsy.com on outubro 2nd, 2010 at 11:54 #
[...] This post was mentioned on Twitter by Denis Ferrari and Denis Ferrari, Denis Ferrari. Denis Ferrari said: http://bit.ly/beew3A Persistência Pugável – De longe o maior post que eu já fiz no @heroisdati. #SouDev #EF4 #NHibernate #DotNet [...]
Rafael Noronha on outubro 4th, 2010 at 22:52 #
Oi Dênis, como vai!? Achei a solução elegante, mas acredito que em alguns cenários perderá a praticidade. Ao enxergarmos a api do ORM, temos mais flexibilidade e poder. Abraços e continue blogando
Denis Ferrari on outubro 4th, 2010 at 23:48 #
Oi Rafael, Bom, vamos lá: – Não usei a abstração pela abstração, você pode ver que os contratos são menores inclusive do que na descrição dos Patterns. Fiz uma abstração simples no domínio e usei o poder dos ORMs em uma camada separada, não só centralizei a tecnologia em um ponto p/ organizar o projeto e proteger o domínio como também deixei ambas as partes mais fáceis de serem testadas, já que são partes desacopladas. – A solução é uma prova de conceito. Realmente não penso todos os dias em trocar o meu ORM, mas com um exemplo grande como este, mostro que é possível realizar alterações de forma bem mais simples até mesmo em uma camada tão crítica quanto essa. – Essa forma de trabalho não é uma regra, as regras são ditas pelos projetos, só apresento uma opção. Abraços!
Vinicius Ribeiro on março 17th, 2011 at 18:22 #
Parabéns pelo post. Excelente nível! Com relacao ao comentario do colega Rafael, a abstração se faz necessária pois é requisito deste projeto que se tenha persistência plugável. Como disse o Ferrari, as regras são ditas pelos projetos.
Cristiano Martins on abril 14th, 2011 at 18:50 #
Muito bom este post. Vou aplicar alguns recursos que você usou.
Rafael L on abril 29th, 2011 at 15:37 #
Achei interessante o artigo mas um item me chamou a atenção por me parecer incoerente. Na classe RepositorioLinks você possui uma propriedade do tipo IUnidadeDeTrabalho, porém na propriedade Contexto, você força um casting para a classe UnidadeTrabalho, ou seja, não serve qualquer classe que implementa a interface pois você exige que seja do tipo UnidadeTrabalho ou derivado, então não tem utilidade a interface IUnidadeTrabalho.
Denis Ferrari on maio 1st, 2011 at 20:33 #
Oi Rafael, A interface é o contrato que estabelece a comunicação entre as camadas. Não há problemas em realizar o cast pois todas as classes em questão estão na mesma camada, a de persistência. Abraços!
Leandro Prado on agosto 18th, 2011 at 10:09 #
Muito bom o artigo! parabéns Me restou uma dúvida, qual a utilidade da interface IUnidadeTrabalho?? não consegui entender qual vai ser a sua utilidade..
Denis Ferrari on setembro 22nd, 2011 at 12:14 #
Oi Leandro, A interface faz a abstração da Sessão no NHibernate ou do Contexto no EF. Abraços!
Jose on fevereiro 2nd, 2012 at 8:43 #
Bom dia. Gostaria de saber como esta implementação se comportaria em um projeto aspNet e em um projeto MVC. Teria alguma coisa a ser alterada devido ao dispose depois da renderização da janela? Como fica o contexto da unit of work se o objeto vai ser terminado com o termino do processamento no request da pagina?
Denis Ferrari on fevereiro 15th, 2012 at 20:45 #
Olá José, Você pode usar o padrão session-per-request para resolver isso. Abraços!
fernando mondo on março 29th, 2012 at 18:38 #
Oi, eu sei que este post é antigo e tal, mas poderia me tirar uma dúvida? A infra conhece Percistencia que conhece Dominio então Dominio não pode conhcer Infra? (se não da referencia circular) no desenho do DDD presente no livro do Eric Evans o Dominio conhce a infra.
Denis Ferrari on abril 14th, 2012 at 16:51 #
Separação física e lógicas são diferentes. Você pode ter o mesmo namespace em dlls diferentes. Outra questão, muito do que é dito pelo Evans é sobre responsabilidades, não divisão física. Sendo assim, você poderia ter um projeto com ddd em um único projeto físico. Abraços!
Slipmp – Marcos Paulo – MCP, MCTS e MCPD » Inversão de Controle (IoC) e Injeção de Dependência (DI) – Diferenças on abril 23rd, 2012 at 14:05 #
[...] bom também que mostra um caso prático que utiliza todos os Patterns abordados aqui nesse artigo http://www.heroisdati.com/persistencia-plugavel/ lá aplica todos esses padrões, mostrando um cenário prático na qual eu apoio a usar os padrões [...]
Leonardo Aidar on maio 18th, 2012 at 10:14 #
Denis primeiramente quero te parabenizar a persistência plugável ficou show e estou utilizando ela em meus projetos. Outra coisa queria te perguntar uma coisa você não acha que IUnidadeTrabalho, IRepositorioLinks nao ficariam na própria camada de persistência pois não tem nada muito a ver com o domínio? E a camada de infra eu tentei fazer diferente adpatar pra minha arquitetura e colocá-la na camada de persistência só que gera uma dependência circular você tem alguma idéia de como resolver isso senão quiser não tem problema eu que inventei mesmo!
Denis Ferrari on maio 21st, 2012 at 12:10 #
Olá Leonardo, Na verdade, a unidade de trabalho é um conceito de Infra implementado com tecnologia de persistência. Já os repositórios, são necessidades de domínio resolvidas com tecnologias de Infra. Já tive problemas de referência circular, mas foi devido a forma como decidi usar o meu framework de DI, com referências diretas. Quando resolvi isso, tive uma visão mais clara das camadas, suas responsabilidades e como não era necessário adicionar algumas referências. Abraços.
felipe on junho 9th, 2012 at 21:40 #
Denis a professora Denise da FAESA disse que não se manipula ou não se declara propriedade ID em classes de negócios, pois ID é um atributo que se refere a uma chave primária, e chave primária é coisa de banco relacional, a sugestão dela é que o ID ou a suposta chave primária manipulada em objetos deve ficar nas classes de acesso a banco(a famosa DAL), dessa forma, num futuro próximo quando quiser por exemplo mudar de um banco relacional para um banco OO essa propriedade ID não vai ficar obsoleta ou não vai ser necessário modificar as classes de negócios.
Allan Gomes on junho 24th, 2012 at 18:10 #
E se caso eu precise mapear as minhas classes Model no Dominio ? então vou ter que acabar usando o Entity na minha camada dominio. como resolvo esse problema ?
Denis Ferrari on junho 29th, 2012 at 15:07 #
Conceitualmente ela está certa. Na prática, nunca chegamos a um modelo 100% puro, mas não é necessário. Nenhuma alteração é tranquila, ainda mais passar de um banco relacional para OO. Abraços!
Denis Ferrari on junho 29th, 2012 at 15:09 #
Desculpe Allan, não entendi sua pergunta.
Wesley on agosto 17th, 2012 at 3:53 #
Denis……muito bom o post……. Me tira uma dúvida…… Vamos supor que eu uso session-per-request……em um projeto MVC….. e no caso do NH, eu criaria a session no Global.asax. Como minha camada de persistência teria conhecimento desta session para persistir os dados? transportava esta session pelas camadas, até chegar na camada de persistência?
Denis Ferrari on agosto 19th, 2012 at 19:12 #
Tenho que fazer um post sobre isso, mas não é tão complicado. O próprio Ninject, que tenho utilizado ultimamente, trata quase que automaticamente a aplicação do pattern session-per-request. Com uma bosca rápida no Google você acha alguns exemplos. Abraços! Deixe um comentário
|
|