Premature Encapsulation is the Root of All Evil

Estava lendo esse artigo:
http://www.adam-bien.com/roller/abien/entry/premature_encapsulation_is_the_root

Ok, o texto é bem exploratório, mas me fez pensar.

Em 12 anos trabalhando profissionalmente na área posso contar nos dedos quantas vezes precisei alterar a base de dados do meu programa. Nessa única vez, mesmo com todos os DAOs, o trabalho se mostrou hercúleo e praticamente inviável. E olha que estava trocando de um banco que suporta o supostamente padrão SQL, por um outro de mesmo tipo.

Por isso, pergunto aos senhores. Algum de vocês já foi realmente beneficiado por usar DAOs? Realmente justifica-se a complexidade adicional inserida no código, os frameworks para gerencia-los, e demais problemas que o aumento da complexidade como um todo geram?

O real benefício dos DAOs pode ser melhor visualizado quando você tem mais de uma fonte de dados (além do tradicional banco de dados relacional). Imagine recuperando informações sobre seu modelo de mais de uma fonte - você vai perceber os benefícios do DAO e do DomainStore.

Ser capaz de abstrair o acesso aos dados utilizando componentes comuns e reutilizáveis é uma grande vantagem. Sem contar que, aliado a um modelo de entidades bastante coeso e bem modelado, você facilita muito o desenvolvimento das camadas superiores. Eu tenho sempre DAOs “genéricos” disponíveis no código, e eles facilitam muito o desenvolvimento. O que eu NÃO faço é construir DAOs para diferentes entidades sem haver uma necessidade justificável para isso.

Acho que o grande problema com DAOs é que hoje eles estão supervalorizados; serviços com acessos à contextos de persistência costumam substituir código de DAOs com facilidade e presteza.

Eu uso DAO para não ter código de Hibernate/JPA/ETC espalhado pela aplicação inteira.

prefiro ter um DAO e fazer

findByXPTO

do que

q = em.createQuery("asdadasdasda");
q.setParameter(112321,1231231);
q.setParameter(112321,1231231);
q.setParameter(112321,1231231);
q.setParameter(112321,1231231);
q.getResultList();

Eu concordo com o texto, e acho que isso cai naquela discussao do outro topico sobre daos e repositorios, pq eu preciso a abstracao das camadas dao e etc… se eu tenho uma api simples como jpa ou hibernate pra trabalhar?

No principio eu discordava, achava que precisava de um repositorio e ele delegava para os daos, ai eu tinha as interfaces dao, as implementacoes do hibernate e por ai vai, mas a unica coisa em que isso resultava era trabalho extra. Passei a escrever os metodos de busca diretamente na implementacao do repositorio e pronto. Bem mais simples e nao perco em nada a organizacao.

Agora vale deixar claro pra quem for ler o texto, pra nao confundir o nao encapsulamento prematuro com bagunca. Nao eh porque vc nao precisa de toda uma hierarquia de interfaces e implementacao do pattern dao que voce vai ficar enfiando chamada a api do hibernate/jpa/etc no meio das regras de negocio.

Usei os daos como exemplo, mas isso serve pra qualquer camada adicional que seja feita so por modismo, ou porque alguem “leu em algum lugar”.

Sobre mudar o banco de dados eu ja precisei fazer mais de uma vez, do firebird para o sql server porque o cliente tinha a licenca e nao queria abrir mao e depois passei a usar o postgre como banco padrao. O tempo que levou foi o de criar o esquema no outro banco e alterar a configuracao do hibernate. Claro que isso foi gracas ao hibernate e nao aos daos, mas eh uma coisa que acontece.

Olá. Eu ainda tenho pouca experiência nessa área, mas acredito que a criação de camadas de abstração deva ser algo que se justifique de acordo com a necessidade. Pelo que entendi no artigo o cara meio que critica a utilização do padrão DAO por “costume”, sem que se reflita antes sua real necessidade naquele contexto, ainda mais em situações nas quais já temos outras camadas de abstração como a JPA.
Mas mesmo assim eu acho interessante quando possível ter um DAOGenerico que encapsule o máximo da JPA ou JDBC, dependendo do projeto.

Tipo:

DAO dao = new DAO();
Cidade cidade = dao.localizar(Cidade.class, "CidadePorNome", "Cabo Frio");
List<Cliente> clientes = dao.listar(Cliente.class, "ClientesPorCidade", cidade);

E nos bastidores então tratar as chamadas da JPA.

[]'s
Fred

Eu acredito na necessidade de daos somente quando existem diversas consultas repetitivas nos códigos, quando se usa um EJB Container, onde cada consulta esta atrelada a um metodo e vc pode injetar os beans em outros beans, não vejo necessidade alguma de usar um DAO. Mas em uma aplicação para evitar diversas consulta repetitivas acredito que DAOs para centralizar as consultas são necessários sim.

[]'s

Claro, foi para isso que os DAOs foram feitos. Mas o questionamento é justamente esse. Na absurda maioria das vezes, você não tem mais de uma fonte de dados. Ou não vai mudar o seu acesso. Ou, mesmo com os DAOs, o trabalho vai ser extremamente complexo (escrever vários DAOs também é bastante difícil, especialmente se sua aplicação já passou por otimizações para aquela base de dados).

Bastante vantagem? Mas é uma vantagem para algo que potencialmente você não vai usar?

É, acho que é mais ou menos por aí também. A gente vê muita gente no GUJ partindo para DAOs, VOs e frameworks complexas sem nem sequer se perguntar o porque.

Claro, foi para isso que os DAOs foram feitos. Mas o questionamento é justamente esse. Na absurda maioria das vezes, você não tem mais de uma fonte de dados. Ou não vai mudar o seu acesso.
[/quote]

Isso é parcialmente verdade. “Maioria das vezes” … hummm …

No meus sistema é raro quando eu não preciso ter mais do que uma fonte de dados. A razão é simples : testes automáticos.
Não vou acessar o banco real durante os testes, nem me vou dar ao trabalho de ter um banco de testes. Eu não estou testando o acesso ao banco (eu confio no jdbc) e sim as features do sistema. Então é comum utilizar algum tipo de objecto que encapsula o acesso a dados ( não necessáriamente um DAO ou um DomainStore) e posso mudar sem causar problemas no resto da aplicação.

O texto que referiste está equivocado. Ok, é verdade que a maioria das pessoas não sabe construir uma arquitetura e abusa de pseudo-padrões ( cosias que o cara acha que implementa o padrão X, mas não realidade não) Mas quando vc tem que testar o sistema de forma automática o uso de padrões não apenas é correto como é mandatório. O mecanismo de teste ( O Junit) cria um ambiente de execução diferente do habital para a aplicação e isso força configurações diferentes e o “encaixe” de peças diferentes na estrutrua. Trocar um serviço real que conecta ao sistema X por outro que responde um objeto fixo e simula a comunicação , ou substituir o serviço real por um que lança execptions é prática comum. Esta necessidade de agradar a dois clientes/ambientes força um design melhor e o melhor design é automáticamente mais flexivel.

Usar OO corretamente nunca fez mal a ninguem. Esta conversa de que usar padrões de mais é errado é de quem não sabe usar padrões. É desculpa de mau pagador (aluno).

Se vc quer testes automáticos , vc os quer rápidos. E isso passar por trocar a fonte de dados.
Por outro lado, ao fazer teste é prático ter um conjunto de dados que simulam as várias situações dos testes. É dificil ter estes dados e são construidos junto com os testes num processo iterativo. O ponto é que vc precisa ter controle sobre eles. Isso normalmente pode ser feito substituindo a fonte de dados.

Não estou dizendo que é facil. É dificil. E por isso a maioria das pessoas não o faz. E por consequencia não usa testes automáticos. Mas se vc usa testes automáticos vc é inevitável usar um design mais flexivel. Às vezes isso o obriga de não usar alguma framework da moda só para poder ter testes automáticos. É um trade-off importante. Tlv o mais importante ao construir uma aplicação OO.

Uma boa conclusão que podemos tirar dessa discussão é a necessidade de refletir bastante antes de começar a codificar, sobre quais as reais necessidades do seu projeto. E não adotar tecnologias e padrões baseados em o quão legal isso vai se encaixar no seu currículo, ou o quão complexo o sistema PODERÁ ficar no futuro (pseudo-requisitos), etc.

O que acontecia com frequência até pouco tempo atrás é o uso exagerado de “buzzwords” no ciclo de desenvolvimento, em caráter experimental, afim de se familiarizar com uma tecnologia/pattern para conhecimento próprio/da empresa, sem avaliar a real necessidade da aplicação da mesma a longo prazo no projeto. Tenho encontrado esse comportamento cada vez menos em equipes de novos projetos, o que me leva a crer que a comunidade está se conscientizando nesse sentido, e arquitetos malucos estão perdendo espaço para projetos funcionais e ágeis :wink:

Concordo parcialmente, é fácil vc ter uma base em mysql e nos testes usar H2 ou HQSQL ou outras similares, onde vc tem total controle e pode testar repetidamente as mesmas situações.

Se não existe nenhum código otimizado especificamente para uma base de dados, apenas mudando o persistence.xml vc resolve esse problema, porém em aplicações mais complexas isso não é suficiente e realmente acaba sendo necessário uma abordagem diferenciada.

De qq forma um código otimizado pode não funcionar na base de testes, por algum tipo de limitação e no final vc pode acabar tendo de ter uma base Mysql tb para testes.

O problema que o DAO visa resolver não é trocar a fonte de dados. Mas interoperar entre fontes de diferentes tipos.

Geralmente, quando faço testes unitários, rodo no mesmo tipo de fonte. Isso é, se vou usar SQL server, tenho uma base de testes no próprio SqlServer. Se vou usar MySql, terei uma base de testes no próprio MySql.

Isso evita surpresas também. Quanto mais próximo os testes forem do ambiente real, melhor.

E, para trocar entre fontes de mesmo tipo, o DAO simplesmente não é necessário.

[quote=ViniGodoy]O problema que o DAO visa resolver não é trocar a fonte de dados. Mas interoperar entre fontes de diferentes tipos.

Geralmente, quando faço testes unitários, rodo no mesmo tipo de fonte. Isso é, se vou usar SQL server, tenho uma base de testes no próprio SqlServer. Se vou usar MySql, terei uma base de testes no próprio MySql.

[/quote]

Normalmente uso uma implemenação em memoria com Map. Previamente carrega com o dados de teste. A minha fonte de dados não é o banco de dados. Então sim, diferentes “tipos” de fonte de dados. ( é que para mim o tipo está incluso da def de fonte de dados)

Não necessáriamente. Se vc inclui o banco em todos os testes vc inclui um overhead desnecessário. A menos que vc não confie no seu ORM não ha necessidade dessas chamadas todas. Se vc não confia realmente no ORM vc faz testes apenas relativos ao ORM e não do resto do sistema.
Além disso vc deve ter testes de integração e ai sim todo o processo é exercitado. Mas para criar classes e ver se elas funcionam comunicar com o banco pode ser overkill.

Não tão rápido … Fazer um select para paginação em SQLServer é bem diferente do que para Postgress. O DAO é suposto encapsular esta diferença. Então mesmo para fontes do mesmo tipo (“banco de dados”) ainda precisa ter diferentes implementações. ( Claro, com um design bom, vc minimiza essas implementações repetitivas , pelo uso de dialectos, por exemplo, mas mesmo assim …).
O ponto é que no geral vc sim precisa mudar de DAO (aliás é para isso que ele serve). NO caso particular e dependendo da sua arquitetura e maturidade do seu framework de negocio / applicação pode não ser necessário. Dizer que não é necessário sempre, é ir longe demais.

Tudo isso é resolvido facilmente se você não programar para banco de dados. Se vc programar para interfaces e via OO esse problema não se coloca.

Acho que eu entendi o texto diferente de voce, Sergio. Pelo que entendi o autor se refere ao encapsulamento prematura daquilo que nao precisa ser encapsulado. Sem pensar nos porques do que esta sendo feito.

Por exemplo, (e eu mesmo ja defendi essa pratica aqui) por que eu preciso de toda a abstracao e encapsulamento das camadas de acesso a dados definidas pelo padrao DAO, se os proprios frameworks ORM ja me dão esse encapsulamento, o unico cuidado que eu preciso é nao misturar os codigos dessas apis com as minhas regras. Mas nao creio que seja isso que foi indicado no texto.

Talvez eu tenha lido de forma relapsa, mas a intencao do autor era indicar o porque de encapsular o que ja esta encapsulado, era de criticar o fato de se fazer alguma coisa só porque em algum lugar se leu que é assim, sem questionar, sem testar outras formas, sem buscar outras solucoes. E essa é a coisa que mais vejo por ai. Vou encapsular porque diz que encapsular é certo. E essas coisas nos levam a encontrar, como encontrei, uma aplicacao dividida em duas pra encapsular a regra num web service, que jamais sera usado em outra aplicacao, apenas pra separar apresentacao da logica.

Eu já li um texto que toca mais ou menos nesse assunto, mas se chama Premature Flexibilization Is The Root of Whatever Evil Is Left. Basicamente, é a atitude de deixar as coisas muito complexas sobre o pretexto de deixar flexível, só que a própria complexidade se torna fonte de rigidez.

Eu não acho errado o DAO, acho errado outras coisas que o pessoal faz com persistência, do tipo: gerar DAO em cima do Hibernate, ou então criar um DAO genérico…

Ou pior ainda, para cada tabela do banco, criar um CRUD pro usuário. Ora, CRUD é raro num sistema bem feito e se localiza mais ou em conceitos que não são o core ou em configurações. Os outros casos são regras de negócio que contém algum tipo de operação de persistência, mas que fica oculto para o usuário. O problema é que basicamente os programadores preferem seguir um script previsível de menus com “inserir blablablá”, “alterar blablablá”, “consultar blablablá” e “remover blablablá”, esquecendo até mesmo que o usuário não pensa em termos de banco de dados. Um sistema com apresentação ao usuário ruim desse jeito, não vale nem a pena criar DAOs para encapsular regra do banco. O programador expôs o banco ao usuário!

Quando ocorre uma preocupação da necessidade do usuário, e o sistema usa termos familiares, não dos desenvolvedores, mas de quem a utiliza. É natural aparecer a famosa “regra de negócio”, onde se percebe que é esquisito misturr essas regras com acessos “crus” de banco. O DAO passa a ser uma solução natural para resolver isso.

Sendo simplista:
Use um ORM (JPA/TopLink/Hibernate/etc), e implemente todo o acesso aos dados no repository usando o seu ORM.

Pronto falei!!

Eu estava me referindo a Postre e SQLServer como fontes de dados diferentes.

Como eu disse, eu ja precisei fazer essa migracao num sistema relativamente complexo. Foi feito sem traumas, mas gracas ao hibernate, nao gracas aos daos. E claro, eu tinha controle absoluto sobre os esquemas dos bancos.

Se tivesse sido feito diretamente com JDBC o trabalho seria bem maior, por mais que eu tivesse aplicado o padrao da maneira indicada e nao sei o que mais, ainda assim eu teria que reimplementar todos os daos, um a um. Eu, com certeza precisaria ter a hierarquia SqlServerDAO e PostgreDAO, e nao tem nada de facil nisso.

Acho que esses “root of all evil” que tem surgido. Otimizacao, flexibilizacao, encapsulamento, vem apenas confirmar uma regra primaria do Extreme Programming: KISS (keep it simple, stupid).

Editado só pra conclusao:
O que me dá medo mesmo são as más interpretações dessas coisas.

[quote=sergiotaborda]Não vou acessar o banco real durante os testes, nem me vou dar ao trabalho de ter um banco de testes. Eu não estou testando o acesso ao banco (eu confio no jdbc) e sim as features do sistema. Então é comum utilizar algum tipo de objecto que encapsula o acesso a dados ( não necessáriamente um DAO ou um DomainStore) e posso mudar sem causar problemas no resto da aplicação.
[/quote]

Fugindo um pouco do assunto principal do topico, mas nao pude deixar de notar.

Com um banco de testes voce nao vai testar o jdbc, vai testar se seu esquema e se seu mapeamento objeto-relacional estao corretos, vai manter os testes la para que eles indiquem que continuam corretos a cada alteracao no modelo de objetos ou no esquema do banco. E voce nao pode fazer isso no seu banco de desenvolvimento, menos ainda no de producao, porque nao vai conseguir manter os dados sempre no mesmo estado para que os testes seja realizadao. Entao é fundamental que haja um banco de testes.

Do contrario seus dados nao existem (baseado na afirmacao de que uma funcionalidade só existe se existe um teste pra ela).

[quote=YvGa][quote=sergiotaborda]Não vou acessar o banco real durante os testes, nem me vou dar ao trabalho de ter um banco de testes. Eu não estou testando o acesso ao banco (eu confio no jdbc) e sim as features do sistema. Então é comum utilizar algum tipo de objecto que encapsula o acesso a dados ( não necessáriamente um DAO ou um DomainStore) e posso mudar sem causar problemas no resto da aplicação.
[/quote]

Fugindo um pouco do assunto principal do topico, mas nao pude deixar de notar.

Com um banco de testes voce nao vai testar o jdbc, vai testar se seu esquema e se seu mapeamento objeto-relacional estao corretos, vai manter os testes la para que eles indiquem que continuam corretos a cada alteracao no modelo de objetos ou no esquema do banco.
[/quote]

Mas afinal você está testando a sua aplicação ou o seu uso do framework X ?
Como eu disse , ou vc confia no ORM que usa, ou não confia. Para testar uma funcionalidade de transação de conta vc precisa de banco de dados ?
Para quê ? Para ter a certeza que ficou gravado ? Ora, o ponto não saber se ficou gravo, é saber se os valores são corretos. E para isso não precisa de um banco de dados. Ou melhor, vc precisa de um banco de dados (definição formal) mas não de um SGDB.

Em sistema orientados a dados até poderia concorda contigo, mas em sistemas direcionados ao tratamento de entidades , não.
Não é fundamental ter um banco de dados SGDB. Um map ou um list resovlem a maior parte dos testes.
Mas como falei antes, se não confia no seu ORM, então, ok, por todos os meios use um banco de dados. Só se lembre de cumprir o mandato de testes que implica em que o estado antes e depois do teste tem que ser o mesmo. i.e. não se esqueça de apagar o que gravou de novo ou gravar o que apagou durante o teste. Agora… vc acha que esse tipo de controle de consistência do banco é algo fundamental ? Eu não acho. Acho chato e sem propósito ( mesmo com DBUnit é melhor não precisa do DBUnit).

Adendo:
O conceito de banco de testes só existe poruqe vc usa o hibernate/jpa que não são domianstores verdadeiros no sentido que não permitem escrever/ler para outos formatos. Se vc tiver um framework com esse poder, vc não vai precisar ter um banco de dados.

Um exemplo muito facil de eliminar o banco durante os testes é usar mocks de repositorios. Como as interfaces do repositorio são simples e orientadas ao dominio implementar o retorno é trivial a maior parte das vezes. Muito mais simples que ficar codificando DAO , DBUnit ou qualquer outra tecnica que teimosamente usa o banco de dados.