Neste post eu vou dar um exemplo de como fazer um modelo de implementação que mantenha a mesma camada de negócios, mas que utilize uma camada de dados que só é conhecida pela camada de interface com o usuário.
É muito comum um programa ter mais de uma implementação específica, principalmente quando os usuários finais podem estar geograficamente em locais de difícil conexão, etc. Enfim, às vezes é necessário fazer o mesmo programa praticamente duas vezes. Somente o fato de pensar em reescrever o mesmo programa já me dá frio na espinha, então estava eu aqui pensando com os meus botões: "Será que não dá pra deixar pelo menos a camada de negócio intocada e eu mudo somente a parte em volta??". Depois de alguns ensaios eu vi que dá sim e não é tão complicado quanto parece.
A idéia aqui é basicamente a clássica implementação em 3 camadas (interface com o usuário, regra de negócio e persistência), mas adicionaremos duas camadas intermediárias que farão o trabalho de seleção e carga da camada de persistência, de acordo com as configurações do programa via web.config ou app.config.
Ok, vamos aos detalhes:
- Camada de interface com o usuário: Sem novidades aqui, você vai implementar do mesmo jeito de sempre, deixando-a com o mínimo de regras de negócio possível (num mundo perfeito, nenhuma regra de negócio...), mas adicionando uma chave ao seu arquivo de configuração, conforme abaixo:

Deste jeito, a camada de factory vai abrir a camada de persistência pelo seu nome de assembly, onde o único trabalho que teremos será o de ajustar a versão do assembly.
- Camada de negócio
Aqui também há pouco a adicionar, mas é importante que todas as classes tenham um objeto privado fazendo referência ao acesso a dados. Então fica praticamente como um cabeçalho das classes de negócio a implementação abaixo:
DLInterface.IDL assemblyToRun = new DLFactory.Factory().getDataLayer("Class1");
DLInterface é o meu namespace para identificar a interface de acesso a dados. Como serão implementações separadas, eu preciso deixar algo em comum entre elas, portanto essa intersecção será feita por uma interface.
- Factory
Aqui é uma novidade: Muita gente simplesmente referencia a camada de dados na camada de negócio e a persistência de dados está resolvida. Sim, em aplicações normais isso funciona muito bem, obrigado. O detalhe aqui é que a camada de dados tem mais de uma implementação. Portanto eu vou colar o código inteiro da minha factory e explicar melhor depois.
Parece confuso, mas é só aparência:
- Camada de negócio
Aqui também há pouco a adicionar, mas é importante que todas as classes tenham um objeto privado fazendo referência ao acesso a dados. Então fica praticamente como um cabeçalho das classes de negócio a implementação abaixo:
DLInterface.IDL assemblyToRun = new DLFactory.Factory().getDataLayer("Class1");
DLInterface é o meu namespace para identificar a interface de acesso a dados. Como serão implementações separadas, eu preciso deixar algo em comum entre elas, portanto essa intersecção será feita por uma interface.
- Factory
Aqui é uma novidade: Muita gente simplesmente referencia a camada de dados na camada de negócio e a persistência de dados está resolvida. Sim, em aplicações normais isso funciona muito bem, obrigado. O detalhe aqui é que a camada de dados tem mais de uma implementação. Portanto eu vou colar o código inteiro da minha factory e explicar melhor depois.
Parece confuso, mas é só aparência:

No método getDataLayer cria uma instância de um assembly (previamente criado por você, óbvio) e retorna a classe passada no parâmetro. Essa classe é referenciada pelo nome mesmo, por isso o método recebe uma string.
O método getAssemblyType é somente uma centralização de código para buscar a chave no web.config (ou app.config). Se você quiser ignorar, sem crise, mas precisará implementar o conteúdo dele mesmo assim.
Os dois últimos métodos fazem parte de um POG (Programação Orientada a Gambiarra) para contornar uma característica bem chata que eu acabei enfrentando. Se a classe não tem nenhuma referência em código das camadas que ela acessa, o Assembly.Load não toma conhecimento do assembly (mesmo que esteja referenciado certinho...)
- Acesso a dados
As camadas de acesso a dados devem ser implementadas com os mesmos métodos, mesmo que alguns não sejam necessários. Isso porque a interface força a implementação do método e a sua camada de negócios vai precisar conhecer uma estrutura mínima para poder acessar ambas as classes sem conhecê-las completamente.
No meu exemplo aqui, eu defini uma interface bem boba, com apenas um método para teste:
O método getAssemblyType é somente uma centralização de código para buscar a chave no web.config (ou app.config). Se você quiser ignorar, sem crise, mas precisará implementar o conteúdo dele mesmo assim.
Os dois últimos métodos fazem parte de um POG (Programação Orientada a Gambiarra) para contornar uma característica bem chata que eu acabei enfrentando. Se a classe não tem nenhuma referência em código das camadas que ela acessa, o Assembly.Load não toma conhecimento do assembly (mesmo que esteja referenciado certinho...)
- Acesso a dados
As camadas de acesso a dados devem ser implementadas com os mesmos métodos, mesmo que alguns não sejam necessários. Isso porque a interface força a implementação do método e a sua camada de negócios vai precisar conhecer uma estrutura mínima para poder acessar ambas as classes sem conhecê-las completamente.
No meu exemplo aqui, eu defini uma interface bem boba, com apenas um método para teste:

O método showMe simplesmente vai mostrar qual é a camada que está sendo acessada.
No final das contas, a arquitetura desta implementação fica assim:
No final das contas, a arquitetura desta implementação fica assim:

Esta dica foi testada utilizando o Visual Studio 2010.
Até a próxima!