O que usar no lugar de VOs?

Ok, deixa eu tentar imaginar um sistema qualquer sem VOs (ou qualquer outro nome sinônimo):

Vamos supor que eu queira saber quais os funcionários que trabalham em uma determinada empresa e rodar a folha de pagamento deles (um exemplo muito comum do dia-a-dia de sistemas CRUD).

Aqui eu já começo com uma instância da empresa. Utilizo o método getFuncionarios nela, que vai a camada de persistência e chama um método que FABRICA instâncias de funcionários. Correto?

Vamos supor que eu decida fazer usando a JDBC diretamente (sem reflection, hibernate, JPA e nada disso). Eu teria que ter setters para todos os campos para poder setar um valor para cada um deles conforme eles são lidos do banco, ok?

Vamos supor que eu pegue uma ferramenta de reflection que o meu primo criou que acessa os campos ou os setters automaticamente (seja por annotations, reflection bruto, xml, bola de cristal ou qualquer coisa). Ele vê a coluna NOME no ResultSet e chama o método setNome do funcionário que ele recém criou. Correto?

Quando a camada de persistência devolver a lista de funcionários para o objeto de negócio da empresa, beleza. O objeto de negócio chama os métodos de negócio que quiser (inclusive getters e setters), mas no geral são métodos de negócio.

Suponha que você queira cadastrar/inserir um novo funcionário na empresa X. Neste caso você instancia o funcionário com todos os campos com valores iniciais 0 ou null, dá um setEmpresa nele, chama o método setIsso, setAquilo e no final chama o método persistir, que chama a camada de persistência e gera uma SQL INSERT INTO …

Para preencher os valores do PreparedStatement do INSERT, será preciso utilizar os getters.

Agora chegamos no ponto: O objeto de negócio tem os campos para armazenar a informação. Setters burros para receber dados da camada de persitência e getters burros para passar dados a camada de persistência. Pronto! Seu encapsulamento foi pro saco porque no fim das contas o que você fez foi apenas criar VOs anabolizados onde você enfiou um monte de métodos de negócio e decidiu chamá-los de camada de negócio.

E então, como corrigir o modelo acima?

cara pra mim isso e tão nebuloso mais o pessoal fala tão bem que as vezes me lembro do chapolim falando da roupa invisivel que apenas os inteligentes podiam ver a roupa…

não consigo imaginar um sistema sem Objetos de “burros” para passar os dados de um lado para o outro…
fica bagunçado… como seu exemplo acima…

“Porque segundo a OO Objetos metodos e dados devem ficar no mesmo Objeto”…
isso e pratico? eu acho que não…

Primeiro, seu encapsulamento não foi pro saco. Se os dados são acessados por métodos, estão encapsulados. Mas ninguém está discutindo encapsulamento, de qualquer jeito. Não vale a pena misturar (mais) um assunto.

E, não, não há o que corrigir. Se o seu modelo é pobre, o seu modelo é pobre, ué. Se 70% do trabalho é fazer persistência no banco, que tal procurar alternativas onde essa tarefa pode ser feita de forma mais simples, hein?

Tu achas que não, é? E isso é argumento, agora? :? Que tal elaborar um pouco mais essa resposta?

ha… na boa… tanto faz…

abraços

Por que não dividir a resposta em uma ou mais situações onde o uso de VOs é indicado?

Por exemplo, eu tenho um sistema que utiliza os seguintes frameworks:

:arrow: JSF
:arrow: Hibernate

e não é distribuído, devo utilizar VOs? E o que dizer de BOs?

Falou

Eu não disse que o modelo é pobre, eu não disse que 70% do trabalho é persistência no banco e mesmo que você troque a JDBC na unha por um JPA ou qualquer outro método de persistência em um layer a parte, o resultado é o mesmo. O que eu tentei expor aqui é que o fato de haver uma camada de persistência força você a ter getters e setters para todos os atributos. E, como exposto no trecho do artigo da fragmental que eu copiei e colei aqui em cima, o encapsulamento vai para o saco.

E o objetivo aqui é exatamente migrar do modelo anêmico para o modelo ideal. O que eu coloquei no meu post anterior já é mais da metade do caminho, pois os BOs e os VOs já foram unificados. Mas ainda não é o modelo certo, pois o encapsulamento ainda tá no saco.

Pq nao existe nenhuma situacao onde usar VOs ou BOs eh indicado. DTOs, outro padrao relativamente proximo, eh pra ser usado apenas em sistemas distribuidos, e mesmo assim apenas quando os dados a ser trafegados precisam de uma granularidade diferente do que a com que o sistema trabalha internamente.

Pq nao existe nenhuma situacao onde usar VOs ou BOs eh indicado. [/quote]

Ok, aprendi OO e sei que isso não faz parte do conceito MAS a sua resposta não deu margem para nada, ou seja, implicitamente quer dizer que quem usa ou usou, cometeu um erro arquitetural!

É isso?

Muito interessante a citação, mas acredito que a sua interpretação foi errada! Veja bem, o VO pode entrar em estado inválido justamente porque ele não contém lógica - apenas getters/setter burros - e portanto não pode “se proteger” de um estado inválido (no modelo anêmico, essa lógica estaria somente no “BO”). Levando em conta que VOs contém getters/setters “burros” para todos os atributos, aí sim podemos dizer que não há encapsulamento.

No caso da lógica modelada num POJO, o métodos de acesso podem (e devem) conter lógica, modelando o mundo real, podendo assim evitar estados inválidos, por exemplo. Dados encapsulados.

É possível evitar a necessidade de getters e setters, mas não vejo grande benefício em fazer isso. Já que os POJOs contém a lógica de negócios, escrever getters e setters “inteligentes” onde necessário são uma garantia de que o encapsulamento não foi “pro saco”, não.

Pq nao existe nenhuma situacao onde usar VOs ou BOs eh indicado. [/quote]

Ok, aprendi OO e sei que isso não faz parte do conceito MAS a sua resposta não deu margem para nada, ou seja, implicitamente quer dizer que quem usa ou usou, cometeu um erro arquitetural!

É isso?[/quote]

Eu já acho que usar VO não é errado. Errado é usar VO e achar que está programando OO. Separar o código entre dados e operações é escrever Pascal em Java. Se é isso que você quer, vai fundo e seja feliz. Mas aí seja honesto e exiba os atributos como públicos em vez de criar pares get/set e achar que está encapsulando alguma coisa.

[quote=rubinelli]
Eu já acho que usar VO não é errado. Errado é usar VO e achar que está programando OO. Separar o código entre dados e operações é escrever Pascal em Java. Se é isso que você quer, vai fundo e seja feliz. Mas aí seja honesto e exiba os atributos como públicos em vez de criar pares get/set e achar que está encapsulando alguma coisa.[/quote]

Então você não está usando TO (VO é outra coisa ha um bom empo), você está usando estruturas de dados, pura e simplesmente. P padrão TO serve para meia dúzia de problemas causados pela complexidade acidental do EJB 2.x, e mesmo para isso existem alternativas.

[quote=victorwss]
Eu não disse que o modelo é pobre, eu não disse que 70% do trabalho é persistência no banco e mesmo que você troque a JDBC na unha por um JPA ou qualquer outro método de persistência em um layer a parte, o resultado é o mesmo.[/quote]

Com Hibernate, JPA e outros ORM, persistência não é nem 5% do trabalho. Dá pra fazer o mapeamento do projeto inteiro em 1 dia (é meio BDUF, mas dá pra fazer). Você está trabalhando com uma abstração de uma entidade, e com isso, pouco importa o que ocorre na persistência. Não é trabalho nosso. É trabalho do framework.

[quote=felipeguerra]Por que não dividir a resposta em uma ou mais situações onde o uso de VOs é indicado?

Por exemplo, eu tenho um sistema que utiliza os seguintes frameworks:

:arrow: JSF
:arrow: Hibernate

e não é distribuído, devo utilizar VOs? E o que dizer de BOs?

Falou[/quote]

Dá uma olhada nos exemplos do Jboss Seam que fica mais claro. Action Frameworks tradicionais forçam um pouco você a usar DTOs pois trabalham com a abstração de um HtmlForm. Com um framework mais moderno como o Seam, a própria entidade pode ser manuseada na camada de view, sem ter objetos burros trafegando de um lado para o outro para serem persistidos na camada inferior. Avoid too many layers.

O Seam tem outros problemas (estamos no mundo real), mas não precisamos de DTOs de jeito nenhum, só nos casos já citados (o que precisa aparecer na tela é algo granularmente diferente da entidade, exemplo, você precisa apresentar os pedidos agregados por cliente, ou mostrar o valor total das compras por mês e local de entrega). Com EJB3 e Managed States das Entidades nem mesmo um sistema distribuído necessariamente precisa usar DTOs. Já desenvolví aplicações grandes com cliente Swing e meus entity beans saiam do servidor e iam para o cliente sem precisar de DTOs.

Somente dados não representam o negócio. Na análise estruturada a gente se enganava achando que o MER representava o negócio. Não representa! Seu modelo de tabelas nada mais é que um conjunto relacional de dados.

Sistemas orientados a objetos são orientados a objetos e não orientados a tuplas. E olha, as tabelas não interessam, bom, para falar a verdade elas interessam muito pouco. Nesse mundo de Hibernate, JPA, iBatis e etc, faz uns bons 3 anos que não ligo para o banco de dados. O que interessa são os objetos.

Exatamente. Que bom que estamos claros no assunto :wink:

Eu ja tive algumas discussões com o autor desse artigo, mas eu aconselho que vcs leiam pois os conceitos são complicados.

http://fragmental.com.br/wiki/index.php/Fantoches
http://fragmental.com.br/wiki/index.php/Evitando_VOs_e_BOs

Segue abaixo uma parte do texto que retrata o problema.
É um bad smell se seus objetos são guiados através de forças ocultas, se seus objetos se
comportam como marionetes sendo manipuladas por uma função-mestre, o mestre dos fantoches

Bem, até aqui ok. Getters e setters burros apenas são uma forma de tornar os atributos públicos mantendo-os privados. Acessar os campos diretamente com setAccessible(true) é uma aberração.

O problema aqui é evitar que o objeto de negócio entre em um estado inválido.
Vamos ver o que o framework de persistência faz para criar um objeto que ele leu do banco de dados:

Hipótese 1: Ele usa o construtor sem parâmetros do objeto de negócio e cria um objeto Funcionario sem nome, sem data de nascimento, sem CPF, sem nada. Ao término do construtor ele já terá nascido inconsistente.
Porém, a medida que o framework lê campos do ResultSet ele invocará os setters e preencherá o objeto de negócio e no fim espera-se que ele seja válido.

Hipótese 2: Ele usa o construtor que recebe um caminhão de parâmetros e cria um objeto Funcionario que em essência já nasce pronto.

Pois bem, o problema da hipótese 1 é que o objeto já nasce em estado inválido. O problema da hipótese 2 é que cria-se um tipo forte de acoplamento entre o framework de persistência e um construtor muito frágil do objeto de negócio (frágil porque qualquer mudança no objeto força uma mudança na assinatura do construtor).

Então agora estamos na frente de mais um problema no caminho rumo a arquitetura ideal: Como evitar de se ter um construtor com uma assinatura frágil e gigantesca e ao mesmo tempo permitir que o objeto nunca esteja inválido, nem mesmo logo após ser instanciado?


Não é nem 5% do trabalho se você considerar que o banco de dados é bem estruturado, não há nada de legado nele, você tem controle total sobre o banco, que não há registros inconsistentes e que nunca é necessário usar-se alguma Stored Procedure para fazer-se alguma maluquice. Quando o negócio é assim, realmente tudo são flores. Mas o problema é quando você tem que lidar com bancos de dados legados problemáticos pertencentes a terceiros, que são usados por diversas outras aplicações e estão em produção. Como lidar com isso sem criar monstros arquiteturais e POGs?
Ok, neste caso acredito que não haja dúvidas de que o certo seria mandar o DBA para a cadeira elétrica e daí o coitado do programador é que vai ter que se virar em lidar com esse tipo de coisa. Mas aí o problema não é só do framework, passa a ser problema do programador fazer o framework conseguir digerir o pepino. Ou seja, o mapeamento objeto relacional é 5% do trabalho SE E SOMENTE SE você tem liberdade para fazer o que quiser com a estrutura do banco de dados.

Se o consrutor não garante a invariante do seu objeto ele deve ser isntan ciado por uma factory, e frameworks de persistência agem como factories.

Ok, então o framework chama a factory.
Mas, logo após o framework chamar a factory, antes de chamar qualquer setter. Em qual estado estaria o objeto?
Ou o framework passa um caminhão de parâmetros ou o ResultSet para a factory e ela se vira?

O que o shoes quis dizer eh que o framework eh a factory.