Ola, no começo da semana eu postei um topico com duvidas de como evitar VOs BOs, apos muita discussao eu nao tinha entendido mas depois de ler tudo novamente e inclusive os outros posts que falam sobre o mesmo assunto eu cheguei a seguinte conclusao e gostaria de saber se isto esta correto.
//Meu servlet ou algo do generopublicclassBeta{//Como eu gravava um objeto em um modelo anemicoprivatevoidanemic(){PessoaBOpessoaBO=newPessoaBO();Pessoapessoa=newPessoa();pessoa.setIdade(30);pessoaBO.save(pessoa);}//Como eu deveria gravar um objeto usando DDDprivatevoidDDD(){Pessoapessoa=newPessoa();pessoa.setIdade(20);pessoa.save();}//Teste de usopublicstaticvoidmain(Stringargs[]){Betabeta=newBeta();beta.DDD();beta.anemic();}}//Meu antigo VO, POJO, DTO ou TO, chamem como quiserclassPessoa{privateintidade=50;privateRepositoriorepositorio;publicPessoa(){repositorio=newRepositorio();}publicintgetIdade(){returnidade;}publicvoidsetIdade(inti){this.idade=i;}publicvoidsave(){repositorio.save(this);}}//Objeto de negocio parte do modelo anemicoclassPessoaBO{publicvoidsave(Pessoapessoa){Repositoriorepositorio=newRepositorio();repositorio.save(pessoa);}}//Meio de como persistir os dadosclassRepositorio{//Grava o objeto em algum lugar qualquerpublicvoidsave(Pessoapessoa){System.out.println(pessoa.getIdade());}}
Observacoes:
A classe repositorio poderia ser uma interface, e vc poderia obter uma instancia dessa forma para abstrair aonde vc grava as informacoes
Mas eu acho que isso nao vem ao caso, o que importa é que uma Pessoa tem que possuir um repositorio e a Pessoa deve ser responsavel pela acoes de persistir um objeto do tipo pessoa ! correto ?
Portando a logica de negocio fica no proprio VO.
Mas questão é mais quando uma logica de negocio de Pessoa for executada… exemplo
Antigamente você teria
e sem BO e VO, você usa:
Já que agora, pessoa possui estado e comportamentos…
Quando você quiser exibir Pessoa num JSP, você manda o objeto e não um VO dele porque simplesmente não há necessiade de um VO, então veja que nesse caso ele seria inutil.
Só não chame Pessoa de VO… Pessoa é um objeto de domínio… VO/DTO tem oturo significado
obs: é importante valorizar as nomeclaturas…
O
oyama
Só acho que o seu exemplo não foi muito feliz…
pessoa.save() não é uma “logica de negócio”. Salvar um dado em um banco de dados no fundo não é um problema do negócio, mas quase um “requisito não funcional” do seu sistema. Na minha opinião não seria errado delegar o “salvar” para outra classe (fora do domínio do negócio).
Se fosse tipo pessoa.definirIdade(20), e isto no fundo salva o dado, para mim ai estaria mais correto.
R
ronildobraga
oyama:
Só acho que o seu exemplo não foi muito feliz…
pessoa.save() não é uma “logica de negócio”. Salvar um dado em um banco de dados no fundo não é um problema do negócio, mas quase um “requisito não funcional” do seu sistema. Na minha opinião não seria errado delegar o “salvar” para outra classe (fora do domínio do negócio).
Se fosse tipo pessoa.definirIdade(20), e isto no fundo salva o dado, para mim ai estaria mais correto.
É por isso que o infeliz do Phillip Calçado nao citou exemplos, pois é dificil retrar a situação atraves de um exemplo.
Porem por mais que o exemplo seja ruim :oops: , eu espero que parte do conceito tenha sido entendido
Agora eu nao entendi o que vc quis dizer em relação a delegar o “salvar” para outra classe (fora do domínio do negócio).
Aonde por exemplo e porque ?
Obs.: Espero que ele nao fique ofendido com o infeliz rsrs
Rafael_Nunes
A abordagem que eu costumo usar, você preserva de certa forma a idéia de um Active Record mas sem acoplar o objeto de domínio com a tecnologia de persistência.
class Pessoa{
void save(){
repository.save(this)
}
}
Leozin
mas utilizando ActiveRecord não transformaria o sistema em uma sopa de camadas? A idéia parece legal, uma entidade “completa” que trabalha “sozinha”?
Bom, pelo menos o DAO a gente elimina com esse pattern. Mas e quanto a BOs/TOs? No blog da fragmental não achei/entendi (ainda) como eliminar isso
cv1
O que vc quer dizer com “sopa de camadas”?
R
ronildobraga
Eu nao entendi muito bem, estou vendo o artigo que vc indicou, vc pode passar mais alguns detalhes
felipec
1 colocação…
Nós não estamos usando mais VO nem BO. Estamos usando um objeto de domínio para cumprir o papel do VO e do BO.
Leozin
O ActiveRecord “parece” não trabalhar com POJOs, ou seja, a tua entidade faz tudo, o que me referi a sopa de camadas era a respeito de você juntar uma entidade do sistema + DAO, tal como mostrado no exemplo
R
ronildobraga
Entendi, obrigado por corrigir.
Eu so me pergunto como ficaria isso mapeado no hibernate, eu teria um Pessoa.hbm.xml referenciando o objeto de dominio e nao mais a um VO ?
sergiotaborda
quote=ronildobraga cheguei a seguinte conclusao e gostaria de saber se isto esta correto.
[/quote]
Sim. Mas pode fazer melhor.
Vc tem pessoa referenciando repositorio só para que pessoa delegue para ele. Não precisa. Use apenas o repositório
Repository r = ..
r.store(pessoa); // store == save
E dentro do método vc decide. Pode usar um properties ou outra coisa para decidir. Isto elimina mais uma dependencia.
Por outro lado vc só precisa de uma instancia de Repositor. O factory deve controlar o numero de instanciações e para simplificar torne os métodos de Repository estáticos e genericos.
Vc pode ainda separar o acesso ao banco com DAO se quiser.
E ficaria assim.
//Meu servlet ou algo do generopublicclassBeta{//Como eu deveria gravar um objeto usando DDDprivatevoidDDD(){Pessoapessoa=newPessoa();pessoa.setIdade(20);Repository.store(pessoa);}}}
Rafael_Nunes
Na verdade no link só tem um breve resumo deste padrão, no livro do Fowler tem uma explicação maior.
Mas em suma, Active Record seria além das propriedades e lógica de negócio, inserir nos objetos de domínio também a lógica de acesso a dados. No próprio livro Fowler diz que active record é uma boa solução para sistemas que não tenham uma lógica de negócio muito complexa, tipo CRUDs, e sugere que para domínios com relacionamentos mais complexos, seja utilizado alguma forma de Data Mapper, como DAO por exemplo.
O exemplo que eu dei utiliza as duas estratégias, porém ao invés de o objeto de domínio conter a lógica de acesso a dados, ele delega essa responsabilidade para um Data Mapper.
R
ronildobraga
1)Eu entendo que o objeto de dominio seja a Pessoa, se colocarmos a funcao salvar dentro do repositio e a funcao salvar necessitar de logica, a logica ficaria fora do dominio, a nao ser que o objeto de dominio seja composto por Pessoa + Repositorio, e por isso eu usei composição pois uma pessoa contem um repositorio.
2)Perfeito, realmente tava pessimo aquela dependencia
3)Tornar os metodos do repositorio estatico e generico, nao entendi como fazer, pode ser mais claro.
Rafael Nunes:
Mas em suma, Active Record seria além das propriedades e lógica de negócio, inserir nos objetos de domínio também a lógica de acesso a dados.
Eu nao achei pratica essa solução, e se eu mudar a logica de acesso a dados, por exemplo: Hoje eu uso hibernate mas amanha eu gostaria de usar JDBC, seguindo o Active Record eu teria de configurar novamente todos os objetos de dominio, nao achei muito inteligente nao :?
Fabio_Kung
Pessoal, não vamos misturar duas discussões né?
Domain Model x BO + DTO é uma coisa.
ActiveRecord (pessoa.save()) x DataMapper (dao.save(pessoa) / repositorio.store(pessoa)) é outra!
Rafael_Nunes
Rafael Nunes:
No próprio livro Fowler diz que active record é uma boa solução para sistemas que não tenham uma lógica de negócio muito complexa, tipo CRUDs, e sugere que para domínios com relacionamentos mais complexos, seja utilizado alguma forma de Data Mapper, como DAO por exemplo.
O exemplo que eu dei utiliza as duas estratégias, porém ao invés de o objeto de domínio conter a lógica de acesso a dados, ele delega essa responsabilidade para um Data Mapper.
Pode substituir na frase acima, lógica de negócio complexa por estratégias arquiteturais mutáveis.(E particularmente, já ouvi bastante esta questão de ‘e se eu trocar de banco’, ‘e se eu trocar de mecanismo de persistência’, ‘e se eu trocar o SO do meu servidor’), mas na prática nunca vi e nem ouvi falar disso, a não ser para softwares que já nascem com a intenção de independência de plataforma. E sistemas simples, eu diria que dificilmente isso vai ocorrer.
E no exemplo que eu dei, caso você troque o mecanismo de persistência, só vai ter de fazer modificações no Data Mapper, como faria caso estivesse utilizando diretamente DAO.
Rafael_Nunes
Fabio Kung:
Pessoal, não vamos misturar duas discussões né?
Domain Model x BO + DTO é uma coisa.
ActiveRecord (pessoa.save()) x DataMapper (dao.save(pessoa) / repositorio.store(pessoa)) é outra!
Na verdade, ambas foram levantadas no post inicial:
F
fabio.patricio
Isso mesmo.
]['s
felipec
ronildobraga:
felipec:
Nós não estamos usando mais VO nem BO. Estamos usando um objeto de domínio para cumprir o papel do VO e do BO.
Entendi, obrigado por corrigir.
Eu so me pergunto como ficaria isso mapeado no hibernate, eu teria um Pessoa.hbm.xml referenciando o objeto de dominio e nao mais a um VO ?
Exato!
mas pra melhorar a frase… “referenciando o objeto de dominio e nao mais um objeto anêmico…”
Porque ja vi muito sistema com objetos anêmicos usarem VOs (nesse caso, o hbm.xml mapeava o objeto anemico e não o VO)
Acho que você entendeu…
sergiotaborda
Rafael Nunes:
(…)
E particularmente, já ouvi bastante esta questão de ‘e se eu trocar de banco’, ‘e se eu trocar de mecanismo de persistência’, ‘e se eu trocar o SO do meu servidor’), mas na prática nunca vi e nem ouvi falar disso, (…)
Em termos de aplicações prontas isso é muito verdade. Mas, existe a necessidade de independencia por dois motivos ortogonais a esses.
Testabilidade. Ao fazer testes não quero usar o banco. Quero usar uma lista predefenida de valores que usarei nos meus testes. Ai eu substitui o meu DataBaseDAO por um TestDAO (que herda de InMemoryDAO) devidamente inicializado.
Vida util. O sistema tem maior vida util quanto mais o seu dominio não depender de tecnologia. Se todas as logicas escritas em Clipper ou COBOL podessem simplesmente ser reaproveitadas mudando apenas o SO, ou o Banco ou a interface gráfica essas linguagens ainda estariam dando cartas. Em Java se mudar o SO vc muda a JVM, se mudar o banco vc muda o DAO, etc… A sua logica de dominio permanece.
Outra coisa que pode acontecer é mudar a versão do banco , por exemplo, e vc quiser usar funcionalidades novas. Ai vc cria um novo DAO que funciona para essa versão mas não para as anteriores. E assim vai.
Se vc pensa que a vida util do seu programa é menos de 5 anos, então realmente, estas considerações são meio sem sentido e vc acaba criando uma aplicação que dura 5 anos por vicios no seu design. A minha opinião é que a vida util não deve depender dessas coisas se for possivel não depender.
sergiotaborda
ronildobraga:
1)Eu entendo que o objeto de dominio seja a Pessoa, se colocarmos a funcao salvar dentro do repositio e a funcao salvar necessitar de logica, a logica ficaria fora do dominio, a nao ser que o objeto de dominio seja composto por Pessoa + Repositorio, e por isso eu usei composição pois uma pessoa contem um repositorio.
O dominio é constituido de 3 "seres" : Entidades , Objetos de Valor (Value Objects , aka VO , mas não é a mesma coisa que DTO) e Serviços.
Entidades é tudo o que tem identidade (chave).
Objetos de Valor são classes auxiliares que agrupam muitos campos, como endereço, por exemplo
Serviços é aquilo que manipula as entidades para atingir um objectivo no dominio.
CRUD é um tipo de serviço com 2 operações que serve para alterar o universo de instancias de entidades e/ou objetos de valor. Mas um sistema real tem mais doque isso. Imagine a classica transação bancária. Vc tem conta (entidade) e dinheiro (objeto de valor) e quer fazer uma trasnferencia (entidade) . Vc cria um serviço que dada duas contas e um valor, cria uma transferencia. Poderiamos dizer que isto é uma operação CRUD especial , mas é mais do que isso, porque o lançamento da transação é um evento que provoca um monte de outras coisas.
Portanto, se a "função salvar transação" precisa de uma logica suplementar antes de persistir a trnasação, isso fica dentro do Serviço.
Atualizando o seu modelo teriamos
//Meu servlet ou algo do generopublicclassBeta{//Como eu deveria gravar um objeto usando DDDprivatevoidDDD(){Pessoapessoa=newPessoa();pessoa.setIdade(20);CRUDServiceservice=...service.novaPessoa(pessoa);}}publicclassCRUDService{publicnovaPessoa(Pessoap){Repository.store(pessoa);}}}
Poderiamos argumentar que o serviço não deveria receber entidades, mas isso é outra conversa que não interessa agora.
Quanto a fazer uma classe Repository com métodos estáticos e genericos:
public class Repository {
DAO dao =...
public static void store (Object entity ){
dao.store(entity);
}
}
R
ronildobraga
Eu acho que vc fragmentou a objeto de dominio novamente, o seu servico esta se comportando como uma classe de negocio, vc so mudou de nome.
Lendo uma parte do artigo novamente eu frizo o seguinte: Um modelo rico traduz o mundo real para software da maneira mais próxima possível, ao utilizar classes de dados separadas das classes de lógica esta vantagem é simplesmente jogada fora. No mundo real não existe lógica de um lado e dados de outro, ambos combinados forma um conceito.
Ainda podemos analizar a seguinte imagem
Aqui nos vemos claramente que o Servlet nao tem acesso ao repositorio, se vc criar essa classe de servico, ela sera dependente dos objetos de valor e das entidades, e o servlet tera novamente que popular um objeto(VO) e depois acessar a logica de negocio(BO) do objeto em outro objeto
Eu ainda frizo mais uma parte do artigo: Num modelo anêmico existem objetos que parecem modelar a realidade, como os VOs, mas na verdade não utilizam a característica básica de objetos que é manter dados e comportamento num mesmo componente: o tão falado objeto!
Observacoes:
Eu nao quero ficar defendendo o artigo, pois acho ele confuso e sem exemplos, mas prestando mmmuuuiitta atenção acho que ele esclarece tudo.
Agora uma coisa que eu nao faço é ficar indicando livros, mandar ler denovo o artigo, tenha mais aulas de OO, leia tudo denovo ou coisas do genero, eu acho que citar partes importantes para tentar esclarecer um assunto nao tem nada de errado, e depois quem estiver lendo esses posts e quiser conhecer como tudo funcione ae sim acho que esta na hora de comprar um livro, ter aulas e etc.
GutomCosta
ronildobraga
Eu acho que vc fragmentou a objeto de dominio novamente, o seu servico esta se comportando como uma classe de negocio, vc so mudou de nome.
Lendo uma parte do artigo novamente eu frizo o seguinte:
Um modelo rico traduz o mundo real para software da maneira mais próxima possível, ao utilizar classes de dados separadas das classes de lógica esta vantagem é simplesmente jogada fora. No mundo real não existe lógica de um lado e dados de outro, ambos combinados forma um conceito.
Na verdade Ronildo, o que o Sergio tentou explicar é que os objetos de domínio necessitam de alguém para coordenar a execução de algumas tarefas.
Para isto faz se necessário um Service.
Imagine o exemplo da transação bancária.
Um objeto Conta, representa a instancia de uma Conta em um banco. Onde ficaria o Serviço de transferência entre 2 contas ?
Em um Service.
Ele vai instanciar as 2 contas e passar o valor de uma pra outra.
Observe que não estamos colocando responsabilidades referentes ao objeto Conta no Service. Ele apenas organiza os passos da execução e delega as responsabilidades para os objetos de domínios.
pcalcado
GutomCosta:
Para isto faz se necessário um Service.
Imagine o exemplo da transação bancária.
Um objeto Conta, representa a instancia de uma Conta em um banco. Onde ficaria o Serviço de transferência entre 2 contas ?
Em um Service.
Ele vai instanciar as 2 contas e passar o valor de uma pra outra.
Observe que não estamos colocando responsabilidades referentes ao objeto Conta no Service. Ele apenas organiza os passos da execução e delega as responsabilidades para os objetos de domínios.
Não entendi direito. O Service neste caso poderia normalment eapenas estimular os objetos, ou seja em vez de:
public void transfere(Conta a, Conta b, int quantia){
a.diminui(quantia);
b.aumenta(quantia);
}
podemos ter:
public void transfere(Conta a, Conta b, int quantia){
a.transfere(quantia, b);
}
sergiotaborda
ronildobraga:
sergiotaborda:
Portanto, se a “função salvar transação” precisa de uma logica suplementar antes de persistir a trnasação, isso fica dentro do Serviço.
Poderiamos argumentar que o serviço não deveria receber entidades, mas isso é outra conversa que não interessa agora.
Eu acho que vc fragmentou a objeto de dominio novamente, o seu servico esta se comportando como uma classe de negocio, vc so mudou de nome.
.
Como o GutomCosta já elucidou não foi isso que eu fiz.
Vc precisa de um terceiro “ser” que manipula os objetos do dominio. Porquê?
Imagine um banco verdadeiro no tempo em que não havia computadores.
Contas eram apenas livros e transferencias eram apenas numeros escritos nesses livros. Se as contas eram livros (objectos inanimados) e transferencias era a escrita de numeros quem escrevia os numeros nos livros ? O escriturário. Ele prestava um serviço importante. Ele tinha que garantir que eram colocados os numeros certos nas contas certas e que a operação era atómica (ou o dinheiro era transferido ou não era. sem mais hipoteses) Quando a operação não era atomica ou as contas não eram as que deveriam acontecia um erro.
Em software. Conta é um entidade, o dinheiro é um valor e o responsável por manipular tudo isto é o serviço. Repare que serviço é ainda da camada de dominio. Ele é o cara que entende o que ha a ser feito, e sabe como fazer. No softtware o serviço é responsável por iniciar uma transação (aqui transação JTA) alterar os valores nas contas e fazer o commit. Repare que esta operação não pode ser responsabilidade da conta porque a conta não sabe o que uma transacção entre contas é ( da mesma forma que um livro não sabe o que um escriturário é).
Mas tem mais detalhes … na realidade uma transacção bancária não é uma operação entre contas é :
verificação de saldo disponivel
verificação de limite
o lançamento atómico de dois movimentos pareados (partidas obradas) um numa e um noutra.
Que em codigo seria mais ou menso assim
transfere(Contaa,Contab,Dinheirom)throwsTransferException{JTATransactionT=...try{T.begin();// a conta tem salso suficiente para pagar ?if(a.limiteSaque()<m){// o limite depende do saldo e do credito do cliente, que por sua vez //depende de outras coisas. Tudo isso é responsabilidade do objeto ContathrowTransferException("Salso insuficiente");}Transferenciat=newTransferencia(a,b,m,Data.hoje());t.executa();Repository.store(t);T.commit();}catch(TransferExceptione){T.rollback();throwe;}}Tranferencia{publicvoidexecuta(){// tira de aMovimentoContama=newMovimentoConta(a,m.times(-1),b,data);// da a bMovimentoContamb=newMovimentoConta(b,m,a,data);Repository.store(ma);Repository.store(mb);}}
A aplicação (a camada de aplicação) chamará o serviço passando os parametros necessários e nunca verá a logica do dominio. Ou seja, o servlet passa a responsabilidade ao serviço (da mesma forma que o atendente do balcão passava ao escriturário)
A “logica de negocio” está na realidade distribuida entre os objectos do dominio , cada uma coma a sua responsabilidade e não mais do que essa.
É preciso deixar claro que o Serviço não é apenas uma mera fachada, ele é o cara que o resto do mundo vê (é o atendente no balcão)
Objetos como Transferencia , MovimentoConta tb existem e é necessário ter um historio deles. Eles são entidades, possuem dados , estado, mas possuem tb tomadas de decisão. Por outro lado, eles existem porque ha que separar a responsabilidade dentro das proprias entidades do dominio.
Conta é apenas um agrupador, o saldo é alterado pelos movimentos de conta , que forma o extrato da conta e portanto têm datas associadas a eles. E cada movimento na sua conta causa um movimento numa outra conta de alguém (partida-dobrada). Se continuar esmiuçando o problema encontrará muito mais entidades necessárias. Se pensar en outros dominios - controle de estoque , por exemplo - verá que algumas entidades se repetem, mas têm papeis diferentes. E tudo isso é a complexidade do dominio que a DDD visa simplificar ao abusar das capacidades oferecidas pela OO.
Quanto ao ActiveRecord. Muito simples: na prática não funciona. A razão é simples: quebra de responsabilidade. Os livros não se guardam a si mesmos nas estantes : os objetos de dominio tb não se devem guardar a si mesmos.
Se tentar usar ActiveRecord de uma forma mais gerenciável ,como vc fez, delegando ao repositorio dentro de pessoa, vai acabar com um monte de cdogio repetido, desnecessário, pois chamar o repositorio directamente dá no mesmo e na prática tem mais vantagem (como dilimitar melhor o controlo o commit e o roolbak da persitencia, etc…)
O ActiveRecord é um padrão mencionado em muitos lugares e tem o seu lugar ao sol em determinados usos (DataSet é um exemplo). Mas refere-se a record (registro) e em DDD uma entidade não é vista como um registro porque a persitência é abstraida e portanto existe uma incompatibilidade natural entre ActiveRecord e DDD.
Daniel_Quirino_Olive
sergiotaborda:
O ActiveRecord é um padrão mencionado em muitos lugares e tem o seu lugar ao sol em determinados usos (DataSet é um exemplo). Mas refere-se a record (registro) e em DDD uma entidade não é vista como um registro porque a persitência é abstraida e portanto existe uma incompatibilidade natural entre ActiveRecord e DDD.
Huh?! :shock:
Dá para explicar isso? Aliás, você realmente entendeu o que você acabou de escrever ou você acredita de fato nisso que você falou?
eliziario
sergiotaborda:
ronildobraga:
sergiotaborda:
Portanto, se a “função salvar transação” precisa de uma logica suplementar antes de persistir a trnasação, isso fica dentro do Serviço.
Poderiamos argumentar que o serviço não deveria receber entidades, mas isso é outra conversa que não interessa agora.
Eu acho que vc fragmentou a objeto de dominio novamente, o seu servico esta se comportando como uma classe de negocio, vc so mudou de nome.
.
Como o GutomCosta já elucidou não foi isso que eu fiz.
Vc precisa de um terceiro “ser” que manipula os objetos do dominio. Porquê?
Imagine um banco verdadeiro no tempo em que não havia computadores.
Contas eram apenas livros e transferencias eram apenas numeros escritos nesses livros. Se as contas eram livros (objectos inanimados) e transferencias era a escrita de numeros quem escrevia os numeros nos livros ? O escriturário. Ele prestava um serviço importante. Ele tinha que garantir que eram colocados os numeros certos nas contas certas e que a operação era atómica (ou o dinheiro era transferido ou não era. sem mais hipoteses) Quando a operação não era atomica ou as contas não eram as que deveriam acontecia um erro.
Em software. Conta é um entidade, o dinheiro é um valor e o responsável por manipular tudo isto é o serviço. Repare que serviço é ainda da camada de dominio. Ele é o cara que entende o que ha a ser feito, e sabe como fazer. No softtware o serviço é responsável por iniciar uma transação (aqui transação JTA) alterar os valores nas contas e fazer o commit. Repare que esta operação não pode ser responsabilidade da conta porque a conta não sabe o que uma transacção entre contas é ( da mesma forma que um livro não sabe o que um escriturário é).
Mas tem mais detalhes … na realidade uma transacção bancária não é uma operação entre contas é :
verificação de saldo disponivel
verificação de limite
o lançamento atómico de dois movimentos pareados (partidas obradas) um numa e um noutra.
Que em codigo seria mais ou menso assim
transfere(Contaa,Contab,Dinheirom)throwsTransferException{JTATransactionT=...try{T.begin();// a conta tem salso suficiente para pagar ?if(a.limiteSaque()<m){// o limite depende do saldo e do credito do cliente, que por sua vez //depende de outras coisas. Tudo isso é responsabilidade do objeto ContathrowTransferException("Salso insuficiente");}Transferenciat=newTransferencia(a,b,m,Data.hoje());t.executa();Repository.store(t);T.commit();}catch(TransferExceptione){T.rollback();throwe;}}Tranferencia{publicvoidexecuta(){// tira de aMovimentoContama=newMovimentoConta(a,m.times(-1),b,data);// da a bMovimentoContamb=newMovimentoConta(b,m,a,data);Repository.store(ma);Repository.store(mb);}}
A aplicação (a camada de aplicação) chamará o serviço passando os parametros necessários e nunca verá a logica do dominio. Ou seja, o servlet passa a responsabilidade ao serviço (da mesma forma que o atendente do balcão passava ao escriturário)
A “logica de negocio” está na realidade distribuida entre os objectos do dominio , cada uma coma a sua responsabilidade e não mais do que essa.
É preciso deixar claro que o Serviço não é apenas uma mera fachada, ele é o cara que o resto do mundo vê (é o atendente no balcão)
Objetos como Transferencia , MovimentoConta tb existem e é necessário ter um historio deles. Eles são entidades, possuem dados , estado, mas possuem tb tomadas de decisão. Por outro lado, eles existem porque ha que separar a responsabilidade dentro das proprias entidades do dominio.
Conta é apenas um agrupador, o saldo é alterado pelos movimentos de conta , que forma o extrato da conta e portanto têm datas associadas a eles. E cada movimento na sua conta causa um movimento numa outra conta de alguém (partida-dobrada). Se continuar esmiuçando o problema encontrará muito mais entidades necessárias. Se pensar en outros dominios - controle de estoque , por exemplo - verá que algumas entidades se repetem, mas têm papeis diferentes. E tudo isso é a complexidade do dominio que a DDD visa simplificar ao abusar das capacidades oferecidas pela OO.
Quanto ao ActiveRecord. Muito simples: na prática não funciona. A razão é simples: quebra de responsabilidade. Os livros não se guardam a si mesmos nas estantes : os objetos de dominio tb não se devem guardar a si mesmos.
Se tentar usar ActiveRecord de uma forma mais gerenciável ,como vc fez, delegando ao repositorio dentro de pessoa, vai acabar com um monte de cdogio repetido, desnecessário, pois chamar o repositorio directamente dá no mesmo e na prática tem mais vantagem (como dilimitar melhor o controlo o commit e o roolbak da persitencia, etc…)
O ActiveRecord é um padrão mencionado em muitos lugares e tem o seu lugar ao sol em determinados usos (DataSet é um exemplo). Mas refere-se a record (registro) e em DDD uma entidade não é vista como um registro porque a persitência é abstraida e portanto existe uma incompatibilidade natural entre ActiveRecord e DDD.
É preciso tomar muito cuidado com analogias, justamente para evitar erros assim. Um modelo OO é justamente isso, um modelo do mundo real com uma finalidade específica e não uma representação fiel. E uma aplicação é um modelo da realidade da mesma maneira que um mapa é um modelo da superfície terrestre. Em um mapa países diferentes são pintados em cores diferentes, porque a eficiência do mapa enquanto modelo da realidade é função direta do quanto ele atende o requisito original (identificar claramente fronteiras entre países), mesmo assim, no mundo real, não conheci ainda nenhum país roxo. Da mesma maneira, em uma aplicação Orientada a Objeto, as atribuições de responsabilidades e os papéis exercidos por cada classe não visam a uma “cópia” do mundo real, mas à obediência à uma série de princípios fundamentais que tem finalidades específicas muito concretas de engenharia. Então, pode ser ridículo no mundo real que uma conta corrente “credite-se um DOC”, mas pode fazer todo o sentido do mundo isso (conta::recebe_doc) em uma aplicação OO, uma vez que Conta é o Information Expert definitivo quando se trata de receber um DOC. De saída, eu ganho em coesão, e também diminuo o acoplamento, o que não é o caso do seu código, coisa que podemos comprovar facilmente ao ver que seria muito difícil escrever Unit Tests para seu exemplo. (Aliás, isso é outra vantagem de TDD, a dificuldade em escrever testes é sempre um sinal de que nosso design poderia ser melhorado).
Outro ponto é que você confunde Serviço com Facade. Embora possam parecer coisas similares, não são. Um Facade expõe um interface simplificada para um subsistema complexo, mas ainda expõe operações em termos de objetos nativos de uma aplicação (por exemplo, se eu faço um Facade para JavaMail, eu tenho uma interface com parâmetros e retornos que ainda são tipos pertencentes ao namespace original de java mail). Já um serviço expõe uma operação de um processo de negócios desacoplando as duas pontas. Por exemplo, se eu tenho um serviço de consulta de crédito, a interface desse serviço deverá ser genérica o suficiente para poder ser acessada por qualquer cliente que tenha acesso apenas a definição do meu serviço, sem a necessidade de ter conhecimento da estrutura interna da minha aplicação. Em outros termos, eu não preciso importar um jar do correio para acessar o serviço de CEP. Mas no seu exemplo, nem mesmo Facade o seu objeto transferência consegue ser, ele é apenas um método disfarçado de objeto, tire o construtor e o método execute do seu objeto Transferência e coloque um método estático no lugar e não vai ter diferença alguma entre as duas versões, o que demonstra de maneira inequívoca o caráter procedural do seu modelo.
Não vou nem comentar ainda a mistura de código de transação com código de negócios. Também é lamentável que em pleno 2007 ainda tenha gente que use controle de transação de maneira programática e não declarativa, isso realmente me dá muito desgosto.
Sinceramente, acho que você deveria ler o livro do Evans, e o Object Design da Wirfs-Brock. Depois de ler os dois, leia Refactorings do Fowler pra consertar esse seu código aí em cima.
Cordialmente,
Marcos Eliziário
R
ronildobraga
Eu recebi uma mensagem privada do Phillip e peço desculpas publicamente pela mensagem se referindo a ele. Eu e o Phillip nao somos amigos e nao nos conhecemos e por isso nao tenho direito de fazer qualquer comentario relacionado a ele.
Para ajudar a tornar isso tudo mais concreto, eu usarei um exemplo funcional. Como todos os meus exemplos, este é um daqueles exemplos super-simples; pequeno o bastante para ser irreal, mas o suficiente, espero, para que se possa visualizar o que está ocorrendo sem cair na complexidade de um exemplo real
Espero que tenham entendido que nao tenho nada pessoal contra o Phillip, pois seus artigos é o motivo da discussão desse e demais posts, porem so acho que os artigos podem ser revisados, assim como todo bom Editor faz.
Se a comunidade inteira entendeu claramente os artigos sem qualquer duvida, peço que ignorem esse post e nao facam comentarios
J2Alex
sergiotaborda,
O que você acha que há de errado na seguinte abordagem:
public class Conta {
...
public void transferirPara(Conta conta, Double valor) {
...
}
....
}
Eu considero isso bem intuitivo e funcional…
sergiotaborda
Se bem entendo vc está dizendo que Orientação a Objetos não visa ser uma copia do mundo real. É apenas uma tecnologia para satisfazer necessidades em informática.
Eu concordaria consigo de estivessemos falando de classes de alguma biblioteca ou classes de infraestrutura. Não concordo se estivermos falando de classes de dominio.
Se nem as classes de dominio têm que traduzir o mundo real, então nem são necessárias em primeiro lugar. Usemos TO e pronto.
Teoricamente isso poderia ser verdade. Mas num sistema real não é assim. Não ha vantagem nenhuma em deixar a conta ser um information expert quando na realidade ela não o é. A conta é apenas um marcador, uma referencia para vários processos dentro do sistema. Ela não contem nenhuma lógica de processo, ela transporta atributos para os processos tomarem decisões.
Mas imprenga “Conta” com um monte logicas. E quando houver uma alteração a manutenção é dificil.
Cara, é uma exemplo rápido, num contexto de um topico de um forum… não vai querer que eu perca meio dia escrevendo um codigo testável , funcional, etc, etc… só para dar um exemplo. Quem souber programar não terá problemas em criar um codigo verdadeiro, funcional, testável, etc etc… o dilema no topico é o que é DDD , qual a sua vantagem/diferença em relação a DTO+BO e não TDD.
Se TDD o procupa neste contexto, sinta-se à vontade de apresentar um exemplo de DDD devidamente programado, que funcione , e seja testável.
Todos temos problemas… é uma maravilha como neste forum todo o mundo gosta de apontar os problemas dos outros. Vc também tem problemas. Vc não leu o topico inteiro. Se tivesse lido iria ver que escrevi:
Eu sei muito bem a diferença entre Façade e Serviço. Isso não é um problema para mim, e pelo visto para si tb não. Só que eu não quiz poluir a discussão com esse detalhe. Eu sei que ao fazer isso iriam dizer que afinal estaria usando objetos que não são do dominio (aka TO) para passar a informação , e voltariamos ao ponto inicial da conversa.
Como o objetivo é entender DDD e no caso, explicar que DDD incorpora a noção de serviço , além de entidade de objeto de valor.
Mas já que vc levantou a lebre … DDD não vive isolado do mundo, e não é a palavra final em desenvolvimento de software. Para que DDD funcione, ou seja, para que haja um foco efetivo no dominio, tem que existir um infraestrutura que lhe dê suporte. Por exemplo, é chato conectar framewroks web com DDD sem alguém no meio que traduza os requests em chamadas ao serviço do dominio. A camada de aplicação é necessária mesmo com DDD. E é por isso que DDD é muita parra e pouca uva. O assunto era explicar como evitar TO e vc simplesmente acaba de mostrar que DDD não evita TO, aliás necessita deles para serem passados como parametros dos serviços.
Lamento Ronildo, eu não queria desapontá-lo antes do tempo, mas o eliziario não quiz brincar do mesmo jogo, então aqui fica a verdade. DDD não evita TO.
A mim tb me dá desgosto que em pleno 2007 existam pessoas que não sabem ler - anafalbetos funcionais - e ficam fazendo comentários fora de contexto. A mim tb me dá desgosto que em pleno 2007 existam pessoas que não sabem distinguir didática de técnica. E eu poderia lamentar profundamente a existência destas pessoas, não fosse a existencia de outras que são inteligentes o suficiente para não colocar o carro na frente dos bois. É vida é f#$@ por causa dos primeiro, mas vale a pena por causa dos segundos, tal e qual este forum.
Sinceramente acho que a sua opinião é válida e correta, mas fora de contexto. Os seus destaques quanto à incorreta implementação do codigo são absurdos porque estamos num forum e não numa empresa discutindo a melhor implementação. Eles são até válidos , mas vc poderia simplesmente corrigi-los ou indicar os problemas , entendendo que é um exemplo de um forum, e que não eu não sou obrigado a escrever codigo profissional aqui. Ainda para mais com tantos profissionais que sabem escrever codigo tão melhor e dar exemplos tão melhores…
Moral da historia, vc deve primeiro ler o topico inteiro para não assumir coisas demais. Depois não deve assumir que o codigo escrito aqui como exemplo é um codigo funcional e depois não deveria assumir que eu não li os livros que vc leu. Se isto continua vou ter que escrever uma assinatura com todos os livros que já li na vida… e por ultimo, vc deveria ser mais tolerante.
quanto às transações: se vc usar um container e transações declarativas, as responsabilidade da transacção sai do serviço. Por outro lado, existem ocasiões em que a transação é um conceito mais amplo que JTA , ou foge ao seu escopro (como transações aninhadas) e tem que ser controlado pelo serviço - da mesma forma que um escriturário lida com problemas como falta de tinta na caneta… Mas tudo bem, eu entendi o seu recado: 'cuidado com analogias". Ignore-se então a chamada a commit e roolbak.
Seria muito bom para quem está lendo este topico que vc , ou alguem, desse um exemplo real de uso de DDD num sistema que tenham ai funcionando (com os devidos testes de unidades e tudo o mais) para que os novatos não caiam no erro de chamar a transação na mão … (como fizeram na javamagazine deste mês … )
sergiotaborda
J2Alex:
sergiotaborda,
O que você acha que há de errado na seguinte abordagem:
public class Conta {
...
public void transferirPara(Conta conta, Double valor) {
...
}
....
}
Eu considero isso bem intuitivo e funcional…
Otimo. Então use. Não tenho nada contra isso.
O meu argumento é apenas :
Isso não mostra a necessidade dos objetos do tipo serviço que existem em DDD. E era esse o meu ponto.
Isso não funciona num sistema real de banco. (eu já tentei usar)
2.1.) Não funciona porque não é responsabilidade da conta fazer essa operação. É como usuário.autentica() : não é responsabilidade do usuário se autenticar, é o serviço de segurança que tem que fazer isso.
Mas , como disse, se funciona para si, use.
Me responda apenas:
quem vai invocar conta.transferePara(outra, valor) ?
E,porque não , outra.transfereDe(conta, valor) ?
J2Alex
sergiotaborda:
2) Isso não funciona num sistema real de banco. (eu já tentei usar)
2.1.) Não funciona porque não é responsabilidade da conta fazer essa operação. É como usuário.autentica() : não é responsabilidade do usuário se autenticar, é o serviço de segurança que tem que fazer isso.
Não, não considero que seja a mesma coisa que usuário.autentica().
conta.transfere(…) pra mim ter a ver com negócio, autenticação de usuário não.
Me explique melhor porque isso não funciona em um sistema real? Cite argumentos concretos que justifiquem isso, por favor.
sergiotaborda
J2Alex:
sergiotaborda:
2) Isso não funciona num sistema real de banco. (eu já tentei usar)
2.1.) Não funciona porque não é responsabilidade da conta fazer essa operação. É como usuário.autentica() : não é responsabilidade do usuário se autenticar, é o serviço de segurança que tem que fazer isso.
Não, não considero que seja a mesma coisa que usuário.autentica().
conta.transfere(…) pra mim ter a ver com negócio, autenticação de usuário não.
Não estamos falando de negocio , estamos falando de dominio.
transfere é uma operação do mesmo dominio de conta, autentica é uma operação do mesmo dominio de usuário. autentica não é uma operação do dominio de conta.
Já expliquei, mas aqui vai de novo. Na realidade uma transferencia não é a alteração do saldo da conta , o qual não é um atributo da conta. Transferencia é a criação de pares de movimentos de conta. Eu preciso saber a conta para construir os movimentos, mas a conta são se altera. Os movimentos são necessários porque criam uma historia do uso da conta: o extrato. O extrato é o que o cliente vê. O saldo é uma função em cima dos movimentos/do extrato. Os movimentos podem ser de diferentes tipos (DOC,TED, interno, externo , etc…) e cada uma tem as suas regras , que nada têm a ver com as contas. Como disse a conta é um identificador que é usado pelo resto do sistema.
Para as funções de report vc não quer a conta, vc quer os movimentos.
Para as funções de contabilidade vc quer a contraparte dos movimentos e não a conta.
Em concreto: uma conta não tem um campo saldo, nem as operações setSaldo(), getSaldo(); Tem o método saldo() que consulta os movimentos (na realidade pode consultar um cache com o saldo, mas não pode alterá-lo). Também não têm as funções aumentaSaldo e diminuiSaldo.
Por outro lado, existem mais informações necessárias à transferencia, como quem a autorizou, quando e porquê.
Para vc como usuário de banco transferencia parece simples, mas para o banco não é. É coisa séria. Esta diferença de visão deve-se ao encapsulamento. O serviço pede apenas como parametros duas contas e um valor, mas o sistema tem muito mais coisas a criar a partir dai.
Eu dei o exemplo da transfrencia bancária porque é classico nestas situações em que um serviço é requerido. Mas sintam-se à vontade de implementar de outra forma.
P.S. Vc não respondeu Às perguntas:
quem vai invocar conta.transferePara(outra, valor) ?
E,porque não , outra.transfereDe(conta, valor) ?
J2Alex
sergiotaborda,
Responderei às suas questões assim que sair do trabalho. Hoje tá meio complicado aqui… rs.
R
ronildobraga
Portanto, quando a logica de negocio envolve mais um dominio, nos criamos entao um servico para este gerenciar estes dominios, e o servlet pode acessar diretamente esse serviço, pois ele faz parte do dominio
Apesar de vc citar que a logica esta distribuida nos objetos de dominio, nos vimos que parte da logica na verdade esta no servico, porem como o servico faz parte do dominio vc pode dizer que a logica continua no dominio. Portanto digamos assim: Uma Pessoa(Objeto de dominio) pode conter um repositorio, porem quando a logica de negocio envolver outra dominio, sera necessario criar um servico que ira gerenciar os dominios e seus respectivos repositorios. correto ?
Realmente DDD nao evita o TO, pois precisamos passar parte dos objetos de dominio atraves de parametros para os servicos do dominio, porem isso so ocorre quando precisamos trabalhar com mais de um dominio, seja eles iquais ou diferentes.
O que eu nao entendi foi Façade e Serviço, o que isso tem haver ?
Porem eu estou com duvida em uma coisa: Servico realmente retrata a situação quando precisamos resolver a logica dentre 2 dominios ? Eu me pergunto isso pois no exemplo inicial vc usou um servico ele resolve a logica de um dominio especifico, por isso eu devo ter feito confusão achando que vc estaria fragmentando o dominio.
//Meu servlet ou algo do generopublicclassBeta{//Como eu deveria gravar um objeto usando DDDprivatevoidDDD(){Pessoapessoa=newPessoa();pessoa.setIdade(20);CRUDServiceservice=...service.novaPessoa(pessoa);}}
eliziario:
Não vou nem comentar ainda a mistura de código de transação com código de negócios. Também é lamentável que em pleno 2007 ainda tenha gente que use controle de transação de maneira programática e não declarativa, isso realmente me dá muito desgosto.
Sinceramente, acho que você deveria ler o livro do Evans, e o Object Design da Wirfs-Brock. Depois de ler os dois, leia Refactorings do Fowler pra consertar esse seu código aí em cima.
É obvio que o exemplo nao retrata uma situção real, cito um trexo que ja citei aqui novamente Para ajudar a tornar isso tudo mais concreto, eu usarei um exemplo funcional. Como todos os meus exemplos, este é um daqueles exemplos super-simples; pequeno o bastante para ser irreal, mas o suficiente, espero, para que se possa visualizar o que está ocorrendo sem cair na complexidade de um exemplo real.
sergiotaborda
Não entendi de onde vc tirou a ideia de que ha mais do que um dominio no exemplo.
Dominio = Entidades + Objetos de Valor + Serviços
Sendo que a quantidade de cada um é variável.
Repositório é uma forma padronizada dos objetos do dominio se encontrarem entre si. (Na prática é uma forma de encapsular a persistencia, mas em teoria não é esse o seu objetivo principal, já que eu posso persistir coisas que não são do dominio e/ou não persistir coisas que uso no dominio)
O repositorio é por dominio.
Servlet não pertence ao dominio. Ele pertence à aplicação ou à intraestrutura ,dependendo das responsabilidades que ele tem.
“logica de negocio” e “logica de dominio” não necessáriamente são a mesma coisa. A logica de negocio é um conjunto de logicas de dominio.
Se a sua aplicação só tem um dominio, a logica de negocio é sobre um dominio. Mas, por exemplo, num ERP existem vários dominio (finaceiro, contábil, produção, estoque, etc… ), e uma só logica de negocio (a integração dos dominios para um determinado fim)
Primeiro : Pessoa não contém um repositorio. Ela usa um repositorio.
Segundo: Se a logica de negocio envolve mais do que um dominio a aplicação é diferente. Exemplo: O dominio Contabilidade vê ContaContábil e o dominio Financeiro , ContaFinanceira , o dominio Contabilidade não vê Produto e o dominio de produção não vê ContaContábil.
Isto, se vc quer isolamento de dominio, que é o amago da questão. Estou supondo que vc quer, pois o objetivo do DDD é encapsular a logica de dominio de forma que elas sejam reutilizáveis. Já pelo reutilizáveis vc entende que ; como cada negocio tem a sua maneira de ser, ele usará os objectos do dominio de forma diferente, mas de forma muito semelhante a um outro negocio.
Voltemos então ao exemplo de um só dominio. Imagine o dominio como um circulo. Vc tem no centro do dominio as entidades. Elas pertencem apenas a um dominio e não mais do que um. As entidades encontram-se umas às outras por meio de um repositorio. Que fica dentro do circulo, sem tocar na fronteira. A fronteira do circulo é onde os serviços estão.
Então, todos os pedidos são enviados pelo exterior (a camada de aplicação) aos serviços. Os serviços usam as entidades e os objetos de valor para executar uma funcionalidade que as entidades por si mesmas não podem. No executar desta funcionalidade metodos das entidades e dos objetos de valor são usados. Esses métodos executam “mini-logicas” e podem delegar a outras entidades e/ou serviços certas funções.
Entenda que o dominio é apenas uma camada do sistema. Existem muitas outras.
Pensemos agora que vc precisa organizar funcionalidades de dois domínios. Aqui vc usaria outro tipo de desenvolvimento. Eventos/notificações (EDD), SOA ou BPM. Isso é outro assunto.
Lembre-se que os objetos do dominio ficam dentro do dominio (dentro do circulo) eles são invisiveis a quem está fora do circulo.
Deste ponto de vista passar Pessoa como parametro para o serviço (ou conta, etc…) é uma violação desse principio, e por isso é errado usar Pessoa num servlet por exemplo. E por isso vc teria que usar um TO para compor os dados de pessoa necessários ao serviço.
Então, um serviço de dominio não tem como parametro os objetos do dominio, mas sim outros objetos utilitários (que chamarei de TO).
Então, o seu servlet lê um request. Algum framework, ou vc na mão, lê os dados do request e decide o que fazer com ele. transforma os dados do objeto request num objecto comestivel ao serviço que executará a funcionalidade necessária. O serviço recebe esse objeto burro e entende o que vc pretende dele. Ele invoca os objetos de dominio necessários e executa o processo. Se ele tiver que retornar alguma coisa , essa coisa tb será um TO, que a camada que chamou o service converterá para um response, que o servlet enviará ao browser.
Quanto à diferença entre façade e serviço , isso é assunto para outro topico. Mas, resumidamente , façade é uma condensação e encapsulação de execuções de serviços. (O objeto que vc usa é uma fachada dos objetos verdadeiros e/ou a funcionalidade que vc requesita é uma fachada para o requesito de várias outras funcionalidades )
Parece que vc está entendendo que CRUD é um dominio. Se é isso, não.
CRUD não é um dominio. No exemplo eu deixei o CRUD ser um serviço cujo objetivo é popular o repositorio de entidades de forma direta ( ou seja, sem logicas acopladas). Mas isso foi um exemplo.
Teoricamente nenhum crud deveria ter logicas acopaldas, mas sabemos bem que isso é mentira. Então a ideia é deixar o dominio validar e responder ao crud. Mas só isso não é suficiente e depende muito da aplicação. Num ERP , por exemplo , fazer crud interage com vários dominios e ai o CRUDService não faz sentido.
Moral da historia: Em boa verdade, CRUD é uma operação sobre o repositorio do dominio. Que tem que ser executada com a conivência do dominio (para validação por exemplo) mas cuja responsabilidade final não é do dominio per se mas sim da aplicação.
GutoCosta
Era exatamante isso que eu queria dizer, acho que não fui feliz na mensagem. Imaginei que seria mais fácil entender da forma que escrevi. :oops:
Valeu.
sergiotaborda
Daniel Quirino Oliveira:
sergiotaborda:
O ActiveRecord é um padrão mencionado em muitos lugares e tem o seu lugar ao sol em determinados usos (DataSet é um exemplo). Mas refere-se a record (registro) e em DDD uma entidade não é vista como um registro porque a persitência é abstraida e portanto existe uma incompatibilidade natural entre ActiveRecord e DDD.
Huh?! :shock:
Dá para explicar isso? Aliás, você realmente entendeu o que você acabou de escrever ou você acredita de fato nisso que você falou?
Eu gostaria de saber como fazer isso, vc pode ser mais claro ?
Eu tb gostaria de saber fazer isso. :lol:
As tecnologias estão ai, o dificil nesta historia é encaixa-las…
RafaelRio
J2Alex:
sergiotaborda,
O que você acha que há de errado na seguinte abordagem:
public class Conta {
...
public void transferirPara(Conta conta, Double valor) {
...
}
....
}
Eu considero isso bem intuitivo e funcional…
Mas aí já estamos na área da arte, onde a arquitetura do software tem um que de cada um. Ou estão pensando mesmo em encarar engenharia de softwares como ciência exata e usar teoremas*.
Porque, se fosse assim, já teríamos IDE’s com fómulas malucas, onde lançaríamos a entrada e sairia o software, exatamente de acordo com os livros do Fowler e do Evans. :lol:
Eu, por exemplo, prefiro utilizar Application Services (Core J2EE Patterns) para definir o workflow entre duas entidades diferentes, ou duas instâncias diferentes da mesma entidade.
Mas a abordagem do Alex também é interessante. Aliás, é bem freqüente ver discussão sobre isso em listas e blogs.
*Com teoremas, eu quis dizer algo como proposições que são observáveis e funcionam da mesma forma nas mais diversas cituações e, por causa disso, praticamente inquestionáveis, como a 1° lei de Newton.
marcelo_mococa
acho esta idéia bem interessante, mesmo para projetos que não usam uma arquitetura baseada em DDD.
sergiotaborda, parabéns pela explicação clara e objetiva.
R
ronildobraga
So retratando um exemplo rapido para corrigir o primeiro exemplo que eu postei
//Meu servlet ou algo do generopublicclassBeta{privatevoidusandoDDDCorretamente(){LogonTOto=newLogonTO();to.setName("fulano");to.setKey(1);to=newUserService().saveUser(to);System.out.println(to.getName());}}//TO, VO, DTO novamente chamem como quiserclassLogonTO{privateStringname;privateintkey=50;...getersandseters}//Entidade do dominioclassPersonEntity{privateintkey=50;...getersandseters}//Entidade do dominioclassLoginEntity{privateStringname;...getersandseters}//Servico do dominio, contem a logica de negocio do dominioclassUserService{publicLogonTOsaveUser(LogonTOto){LoginEntitylogin=newLoginEntity();PersonEntityperson=newPersonEntity();login.setName(to.getName());person.setKey(to.getKey());Repositorio.save(login,person);returnnewLogonTO("fulano gravado com sucesso",to.getKey());}}//Meio de como persistir os dados do dominioclassRepositorio{publicstaticvoidsave(LoginEntitylogin,PersonEntityperson){System.out.println("gravando: "+login.getName()+" chave: "+person.getKey());}}
Creio que seja isso, eu adicionei uma logica bem simples ao servico so para entender que la existe uma logica, seja ela qual for.
O repositorio segue as mesmas observação do primeiro exemplo
Alguns pontos que eu achei melhor nunca mais esquecer
:arrow: Dominio = Entidades + Objetos de Valor + Serviços
:arrow: O dominio é constituido de 3 “seres” : Entidades , Objetos de Valor (Value Objects , aka VO , mas não é a mesma coisa que DTO) e Serviços.
:arrow: Entidades é tudo o que tem identidade (chave).
:arrow: Objetos de Valor são classes auxiliares que agrupam muitos campos, como endereço, por exemplo
:arrow: Serviços é aquilo que manipula as entidades para atingir um objectivo no dominio.
:arrow: CRUD é um tipo de serviço com 2 operações que serve para alterar o universo de instancias de entidades e/ou objetos de valor
:arrow: Repositório é uma forma padronizada dos objetos do dominio se encontrarem entre si. (Na prática é uma forma de encapsular a persistencia, mas em teoria não é esse o seu objetivo principal, já que eu posso persistir coisas que não são do dominio e/ou não persistir coisas que uso no dominio). O repositorio é por dominio.
:arrow: logica de negocio é um conjunto de logicas de dominio.
:arrow: Logica de dominio é interação entre entre os objetos de dominio
J
juzepeleteiro
[color=red]Me retratando: Eu confundi o sergiotaborda com o ronildo no ultimo post. Foi mal Ronildo. [/color]
Sergio Taborda, nome anotado.
Era:
Qual o seu nome completo? Só para evitar que eu injustamente não contrate mais nenhum Ronildo na minha vida.
R
rflprp
uahuahuahuauha :lol:
PS: Tô por fora dessa thread, mas esse comentário foi hilário hahaha
R
ronildobraga
fiz algo de errado :shock: ?
J
juzepeleteiro
fiz algo de errado :shock: ?
Eu confundio você com o sergiotaborda no ultimo post. Foi mal, já alterei o post original.
R
ronildobraga
fiz algo de errado :shock: ?
Eu confundio você com o sergiotaborda no ultimo post. Foi mal, já alterei o post original.
Nao quis dizer se o codigo esta errado, queria saber fiz alguma coisa de errado.
R
rflprp
ele queria ter dado uma alfinetada no outro maluco, só que trocou os nomes.
auhauhah, mas to rindo até agora huahuahuh
sergiotaborda
juzepeleteiro:
[color=red]Me retratando: Eu confundi o sergiotaborda com o ronildo no ultimo post. Foi mal Ronildo. [/color]
Sergio Taborda, nome anotado.
Era:
Qual o seu nome completo? Só para evitar que eu injustamente não contrate mais nenhum Ronildo na minha vida.
Com uma frase vc justificou tudo o que eu disse. Obrigado.
class UserService{
public LogonTO saveUser(LogonTO to){
LoginEntity login = new LoginEntity();
PersonEntity person = new PersonEntity();
login.setName(to.getName());
person.setKey(to.getKey());
Repositorio.save(login, person);
return new LogonTO("fulano gravado com sucesso", to.getKey());
}
}
eu faria assim:
class UserService{
public Person saveUser(Person person){
Login login = new Login();
login.setName(person.getName());
person.setKey(person.getKey());
person.save(); // ao invés de Repositorio.save(login, person);
login.save();
return person;
}
}
Enfim… o exemplo não ficou bom, mas não vi necessidade no TO.
Só faltou o VO (Value Object), só que até agora não lidei com ele ainda. Por isso não tinha um exemplo fácil para colocar aqui. :)
Até +
R
ronildobraga
Thiago Senna:
Mas pra que o TO?
...este é um daqueles exemplos super-simples; pequeno o bastante para ser irreal, mas o suficiente, espero, para que se possa visualizar o que está ocorrendo...
Portanto o meu exemplo nao é real, no caso eu estava demosntrando um servico do tipo CRUD. Logico que existe outros tipos de servicos e para isso vc demontrou um ótimo exemplo.
Porem eu espero que o que vc definiu ali como Person nao seja de fato uma entidade do dominio, pois as entidades do dominio são acessadas somente pelo dominio, se vc obrigar o servico a receber um argumento que seja uma entidade do dominio vc deve presupor que o servlet deve intanciar uma entidade do dominio para acessar o servico, e isso nao é correto com as regras do DDD pois o servlet nao faz parte do dominio para instanciar uma entidade do dominio
:arrow:Objetos de Valor são classes auxiliares que agrupam muitos campos, como endereço, por exemplo
Portanto os VO sao inevitaveis, talves seja errado chamar isso de VO, TO, pois devemos seguir as nomeclaturas, mas é importante saber que esse objeto esta assumindo um papel semelhante quando era trafegado o TO entre as camadas
Se nos prestarmos atencao, veremos que o que vc definiu como person é na verdade um VO
R
ronildobraga
Rafaelprp:
ele queria ter dado uma alfinetada no outro maluco, só que trocou os nomes.
auhauhah, mas to rindo até agora huahuahuh
:lol: eu so estou rindo agora. É engraçado como nao percebemos coisas tao obvias depois de muita discussão, lendo novamente o post, na verdade ela estava elogiando o Sergio.
brunohansen
Meio tarde mas,
Acho que vc ainda ta confundindo o que é um objeto anemico que o Shoes se referiu! Ou eu estou me confundindo!
Com o metodo acima me parece que vc ve o objeto Pessoa como anemico só por ele precisar do PessoaBO para se salvar. Isso não é la bem verdade pois essa é a separação que fazemos com o padrão repositorio!
Moral da história:
Um modelo é dito anemico quando eu tenho uma classe de dados e uma outra casse para manipular esses dados!
Espero ter ajudado ou se eu estiver errado alguem me corrija!
[]s
sergiotaborda
Primeiro é preciso deixar claro que VO (Value Object : Objecto de Valor) não é um TO. Exemplos de VO são : Adress , CPNJ , Range , Money , quase todas as classes principais da biblioteca TimeAndMoney e da Joda Time. São objetos com estado e opeações sobre esse estado. São muito uteis e normalmente podem ser usados para muitos fins. O que os distingue das entidades é que não têm chave e como tal podem ser usados em muitas funções.
Os VO são normalmente campos das entidades que visam simplificar operações do dominio. Outra caracteristica comum dos VO é que são imutáveis. Java tem alguns VO genericos como String , Integer e todos os Number e Date.
Por outro lado, TO não têm chave e além disso não são VO, ou seja, eles são puramente agregadores de dados que visam diminuir a quantidade de parametros de métodos e podem atravessar diferentes andares (tiers) da aplicação por serem serializáveis. Contudo, isto não significa que são sempre necessários. Uma simples string ou um int podem fazer esse papel.
Dito isto, no seu exemplo abaixo LogonTO tem chave. Isso é uma contradição. Se tem chave é uma entidade. Se é um TO não tem chave.
Não ha verdadeira necessidade de usar get/set num TO já que como ele não contém nenhuma lógica , não teriamos o que escrever nesses métodos e portanto não ha necessidade de encapsulamento. Mas isto é um detalhe. No final das contas os get/set nem pesam na JVM, então melhor usá-los por padrão. O ponto aqui é só chamar a atenção que devido ao objetivo do TO não precisamos deles.
Uma coisa que achei estranho é o repositorio ter um metodo para salvar dois argumentos ao mesmo tempo. Eu estava pensando que o uso do repositorio deveria ser
Repository.save(login);
Repository.save(person);
Pois reutiliza o mesmo método.
Eu não entendi muito bem o objetivo do seu serviço (é logar a pessoa, registrar o login da pessoa, ou os dois ?) mas … suponho que se está criando um login para a pessoa. Eis como eu faria:
classUserService{publicvoidcreateLoginFor(Stringusername,byte[]password){// estou pedindo dados em bruto pq este´e um serviço do dominio// será chamado pelo servlet facilmente sem definir TOPersonp=Repositoy.find(Person.class,username);// username é unico, então atua como chave if(p==null){// essa pessoa não existethrownewPersonNotFoundException(username);}PersonLoginlogin=PersonLogin.valueFor(p);login.setPassword(password);Repositoy.store(login);}// outro serviço com os mesmos parametros.// serve para mostrar que quem sabe o que fazer com os parametros é o serviço (o método) publicvoidautenticate(Stringusername,byte[]password){Personp=Repositoy.find(Person.class,username);// username é unico, então atua como chave if(p==null){// essa pessoa não existethrownewIncorrectUserCredentialsException(username);}PersonLoginlogin=PersonLogin.valueFor(p);if(!login.accept(password)){thrownewIncorrectUserCredentialsException(username);}// se está tudo ok, continua. não precisa retornar nada.}}@Entity// o uso desta anotação , introduzido pelo Thiago Senna é umaboa,maseunãousariaaanotaçãodeJPA(persistencia)esimumaanotaçãopuramentededominio.Masissoaiégostoeimplementação,poragoraentenda-seque@Entitysignificaapenas"isto é um entidade de dominio"classPersonLogin{privateKeykey;// Key é um VOprivatePersonpprivatebyte[]password;publicPersonLoginvalueFor(Personp){PersonLoginlogin=Repository,find(PersonLogin.class,p.getUsername())if(login==null){login=newPersonLogin(p,newbyte[0]);}returnlogin;}privatePersonLogin(Personp,byte[]password){// atribuções }publicbooleanaccept(byte[]password){returnArrays.equals(this.password,password);}}
A seguir o codigo a que se refere este post.
ronildobraga:
So retratando um exemplo rapido para corrigir o primeiro exemplo que eu postei
//Meu servlet ou algo do generopublicclassBeta{privatevoidusandoDDDCorretamente(){LogonTOto=newLogonTO();to.setName("fulano");to.setKey(1);to=newUserService().saveUser(to);System.out.println(to.getName());}}//Servico do dominio, contem a logica de negocio do dominioclassUserService{publicLogonTOsaveUser(LogonTOto){LoginEntitylogin=newLoginEntity();PersonEntityperson=newPersonEntity();login.setName(to.getName());person.setKey(to.getKey());Repositorio.save(login,person);returnnewLogonTO("fulano gravado com sucesso",to.getKey());}}//Meio de como persistir os dados do dominioclassRepositorio{publicstaticvoidsave(LoginEntitylogin,PersonEntityperson){System.out.println("gravando: "+login.getName()+" chave: "+person.getKey());}}
sergiotaborda
Eu concordo consigo. Realmente “save” é um método do repositorio.
É isso mesmo. No seu exemplo da idade é otimo para demonstrar DDD , se fizermos algumas alterações …
@EntityclassPerson{DayOfYeardataNascimento;// isto é um VO que contém ano, mes e dia// outros atributos// get/set // retorna a idade que a pessoa teria/terá na data passadapublicintage(DayOfYeardata){returndataNascimento.to(date).duration().years();// mesmo sem saber o codigo de DayOfYear ler a linha acima é simples// esse é um objetivo colateral de DDD}publicbooleanolderThan(Personother){// basta comparar as idades hoje. // a relação se mantém para qq anoreturnthis.idade(DayOfYear.today())>other.idade(DayOfYear.today());}publicbooleanhasBorn(DayOfYeardata){returnthis.nascimento.compareTo(data)>=0;}}
O atributo permite à classe calcular a idade (em qq data) , compara pessoas pela idade e sabe se a pessoa era nascida numa certa data.
Este monte de métodos é só para mostrar o que se pode fazer, mas a escolha de implementar estes métodos deve vir do dominio. Ou seja,
se o dominio exige que comparemos pessoas com base na idade é bom que pessoa permita isso, mas se o dominio não exige isso, não precisa implementar. A ideia é que ao logo do tempo, a classe Person ganhe a funcionadliade necessárias ao dominio.
Um comentário sobre a classes de manipulação de datas. Estas classes por si mesmas representam um dominio (Tempos e Data) e bibliotecas como a Joda Time tentam implementar esse dominio. Só que o fazem usando apenas VO e não entidades. Afinal é estranho dar uma chave para uma data. Estou dizendo isto só para mostrar que um dominio pode usar classes do outro dominio de forma directa se elas forem VO. As lógicas contidas nos metodos da classes da data são coesas para o seu dominio, o que permite o dominio superior muito mais flexibilidade e nenhuma preocupação com a logica interna dos métodos (SoC : Separation of Concerns - Separação de Responsabilidade) (As classes que usei nos exemplos são meramente ilustrativas e não são o uso de nenhuma biblioteca)
R
ronildobraga
rsrsrs e realmente nao era pra ser entendido, pois eu disse: Eu adicionei uma logica bem simples ao servico so para entender que la existe uma logica, seja ela qual for
Portanto eu nem pensei que logica era essa, acho que errei em demonstrar dessa forma, parabens por demonstrar uma logica mais interessante e mais proxima do real sem cair em muita complexidade.
Com as agumentacoes do brunohansen acho que ficou bem claro o que é um modelo anemico, e os exemplos agora retratam algo mais proximo do mundo real.
Thiago_Senna
Então… no meu exemplo o Person é uma entidade. Não vejo problemas caso o controle conheça as classes de domínio. Ao meu ver o que não seria certo era o domínio conhecer camadas acima dele (classes que são da view ou controle).
Quanto aos VO’s acho que os exemplos do Sergio ficaram ótmos.
R
ronildobraga
Então… no meu exemplo o Person é uma entidade. Não vejo problemas caso o controle conheça as classes de domínio. Ao meu ver o que não seria certo era o domínio conhecer camadas acima dele (classes que são da view ou controle).
Thiago, minhas afirmações são baseadas no que eu pude aprender com esses posts, porem por mais que esclarecemos algumas coisas, outras eu sinceramente não sei como resolver.
No final das contas eu não sei responder a sua pergunta :? Eu estou lendo e traduzindo um material sobre DDD(nfoQ Book: Domain Driven Design Quickly), espero que ate segunda eu possa responder essa duvida.
Observações:
Eu tinha publicado a tradução no meu blog, mas acho que estou violando os direitos autorais.
Mesmo que ele seja free para download é necessário pagar uma taxa para impressão.
R
ronildobraga
Entao… ate onde eu li, eu so achei isso relacionado ao assunto, a minha tradução é pessima.
É necessário um complexo particionamento do programa em camadas. Desenvolver uma concepção dentro de cada camada que é coerente e que depende apenas das camadas inferiores. seguir padrões arquitectónico padrão para fornecer um engate avulso para as camadas acima. Concentrar todo o código relacionado ao modelo do domínio em uma camada e isolá-lo da relação de usuário, da aplicação, e do código da infraestrutura. Objetos de domínio, sem resposabilidade de se mostrar, armazenando neles mesmos, administrando tarefas de aplicativos e assim por diante, podem estar focados em expressar o modelo do domínio. Isso permite o modelo envolvido ser rico e claro o bastante para capturar a essencia do conhecimento do negócio e colocoar isso para trabalhar
Segue abaixo o texto original
Therefore, partition a complex program into LAYERS. Develop a design within each LAYER that is cohesive and that depends only on the layers below. Follow standard architectural patterns to provide loose coupling to the layers above. Concentrate all the code related to the domain model in one layer and isolate it from the user interface, application, and infrastructure code. The domain objects, free of the responsibility of displaying themselves, storing themselves, managing application tasks, and so forth, can be focused on expressing the domain model. This allows a model to evolve to be rich enough and clear enough to capture essential business knowledge and put it to work
Portanto, o que vc falou parece ser correto, mas eu sinceramente tenho duvidas quanto a isso, o mais importante é que se essa abordagem estiver correta talves nao precisamos chamar o servico da forma que eu demonstrei.
Eu fiz uma pergunta no site aonde o livro esta disponivel para download para ver se eles deixam publicar as partes mais importantes do livro. Eu tenho varias anotações para publicar, mas vou esperar a resposta deles.
Observações:
Eu acho educado não so mostrar o texto original mas tb o que eu entendi do texto e por isso coloquei a tradução, eu espero que isso seja uma pratica comum pois esse é um forum em portugues e existem pessoas que lem esses textos e que as vezes nao sabem ler em ingles ou entende o texto de uma outra forma.
Thiago_Senna
ronildobraga:
Portanto, o que vc falou parece ser correto, mas eu sinceramente tenho duvidas quanto a isso, o mais importante é que se essa abordagem estiver correta talves nao precisamos chamar o servico da forma que eu demonstrei.
Quanto aos services concordo com você. Eu entendi o service no DDD como algo diferente do que tenho visto por aí. Tenho visto o pessoal dar o nome de Service para as fachadas que separam controle e domínio. Sinceramente entendo que não seja este o objetivo do service. Pelo que entendi usamos o service para agrupar operações mais complexas que exigem uma manipulação de entidades não relacionadas entre si, por exemplo. Acho que colocamos no service aqueles métodos que não combinariam se ficassem em uma entidade... :mrgreen: Será que é isso mesmo?
Exemplificando com código... usando os exemplos que postei anteriormente o controle ficaria assim:
public void storeOwner(Owner owner) {
owner.save();
}
O método storeOwner() tem só uma linha! Não seria melhor eu apagar o método storeOwner() do meu service e chamar direto o owner.save() no meu controle?
O que eu não sei é se fazendo owner.save() dentro do meu controle quebraria alguma regra do DDD ou se o DDD espera isso mesmo! :roll:
pcalcado
Aí que está: existem Services em diversos níveis mas a maioria reside nessa borda.
Services são Façades de negócio, eles encapsulam lógica de negócio que envolve interações complexas. A únida diferença entre um Service e um Façade é que como tudo em DDD um Service tem que ter uma ligação de 1-para-1 com um conceito de negócios.
Thiago Senna:
O que eu não sei é se fazendo owner.save() dentro do meu controle quebraria alguma regra do DDD ou se o DDD espera isso mesmo! :roll:
Em Java eu diria que sim porque não há um meio feliz de se ter este método sem implementá-lo diretamente no objeto de negócio ou fazê-lo parte de alguma infra-estrutura (deixandod e ser um POJO). Em plataformas mais dinâmicas como Ruby não, já que Persistência é um conceito a parte e se vai ser implementado via AR ou DataMapper não interfere no design da classe de negócios (já que o framework ou engine AOP poderia colcoar o método save() dinamicamente no objeto em runtime).
O que você tem que rpestar atenção é que um objeto de negócios chamar um método save(), seja em que plataforma for, não é DDD visto que eles (objetos de negócio) só se preocupam com o que faz parte do domínio (logo seu Service tem problemas).
R
ronildobraga
Thiago Senna:
O que eu não sei é se fazendo owner.save() dentro do meu controle quebraria alguma regra do DDD ou se o DDD espera isso mesmo! :roll:
Muitas duvidas, a imagem da a enteder que vc pode invocar.
Quick DDD pag 31
Um exemplo típico de interação da aplicação, domínio e infraestrutura podiam funcionar dessa forma: O usuário deseja uma rotas de vôo, e pergunta ao serviço na camada de aplicação como fazer isso. Uma parte da aplicação busca os objetos relevantes do domínio da infraestrutura e invoca métodos relevantes para fazer isso ao verificar margens de segurança a outros vôos já registrados. Uma vez que os objetos do domínio fizeram todas as verificações e atualizaram seu status para ?decidido?, o serviço da aplicação persiste os objetos para a infraestrutura.
Observações:
É muito importante ter um linguajar comum, eu entendo que o que vc chama de controle seja a segunda camada(application).
(…) Concentrate all the code related to the domain model in one layer and isolate it from the user interface, application, and infrastructure code. The domain objects, free of the responsibility of displaying themselves, storing themselves, managing application tasks, and so forth, can be focused on expressing the domain model. This allows a model to evolve to be rich enough and clear enough to capture essential business knowledge and put it to work
Portanto, o que vc falou parece ser correto, mas eu sinceramente tenho duvidas quanto a isso, o mais importante é que se essa abordagem estiver correta talves nao precisamos chamar o servico da forma que eu demonstrei.
Destaco a seguinte frase da citação
Para mim esta frase é muito clara e significa que:
Objectos de dominio não podem ter funções que dependam ou se destinem a UI ou IO. (“displaying themselves”)
Objetos de domínio não podem seguir o padrão ActiveRecord (“save themselves”) , nem nada semelhante. A responsabilidade de guardar e reobter os objetos é exclusivamente do repositório (por isso ele existe).
Serviços de domínio (digo de dominio para diferenciar dos de aplicação e outros) não podem depender de tecnologias (" managing application tasks"). E por isso não devem ser implementados dentro de um servlet ou coisas semelhante.
Os controladores (a aplicação) podem enxergar os objetos de domínio, mas as regras (lógicas) de dominio não podem ficar nos controladores.
Se a regra é muito complexa e um só objeto de dominio não é suficiente, precisamos de um serviço. Respondendo ao Thiago, sim, é para isto que servem os serviços de dominio.
Ai entra a questão de diferenciar “logica de dominio” de “logica de negocio” . Se o objeto pessoa me permite saber a data de nascimento da pessoa, a camada de aplicação pode ter um mecanismo que use essa capacidade , algo do tipo
print"olá"if(person.hasborn(Date.today())){
print"Parabéns pelo seu aniversário"
}
Ora , isto é o uso do dominio pela aplicação. É um exemplo muito simples, e tlv isso atrapalhe. A questão é que consultar se a pessoa nasceu hoje é um coisa que a aplicação faz, mas a logica que responde a isso , está no objeto do dominio.
Moral da historia , se a logica pertence ao domínio, ela deve apenas existir nos objectos de dominio (VO, Entidades, Serviços) e nunca em outras camadas. Se a logica é de uso do domínio ela fica fora do dominio, em qq outra camada (espera-se que na de aplicação, mas pode ser usada na de apresentação eventualmente)
O entrave fundamental que parece permear este tópico é dar exemplos de DDD usando persistência. Embora persistencia seja um dominio também, ele é muito especial. Se misturarmos as duas coisas vamos acabando resolvendo que a entidades podem ser ActiveRecords e que o Repositorio não serve para nada (exatamente o contrário do que é afirmado pela DDD e frase citada deixa bem claro). Um bom exemplo para entender a diferença é dar uma olhada na TimeAndMoney API. Embora a Joda Time seja tecnicamente superior, a TimeAndMoney demonstra melhor os conceitos de DDD. Não vão ver la código de persistência.
Thiago_Senna
E quando as dependências das entidades (a implementação do repositório por exemplo) são injetadas via ID usando AOP? Neste caso, mesmo sendo uma aplicação java as suas entidades deixariam de ser um POJO? Posso estar errado, mas vejo esta solução como algo parecido com o que acontece com linguagens dinâmicas, só não tão bonito! rsrs…
Essa parte me deixou confuso. Se eu quero que após uma determinada operação o estado da entidade seja persistido não posso chamar o método entidade.save() e sim o repository.add(entidade)? Ou melhor ainda, a camada de negócios manipula o estado da entidade e no final a camada de aplicação se responsabiliza por delegar a persistência da entidade para a camada de infraestrutura (chamando entidade.save(), por exemplo)?
ronildobraga:
Observações:
É muito importante ter um linguajar comum, eu entendo que o que vc chama de controle seja a segunda camada(application).
Sim sim! O que chamo de controle seria a camada de aplicação. Valeu!
Ótimo! Também penso isso por que vejo muito isso no Grails. Agora o que pega é o detalhe que o Shoes citou sobre java x linguagens dinâmicas. Mas acho que hoje vale a pena (dependendo da aplicação) utilizar uma infraestrutura similar ao que vemos nas linguagens dinâmicas.
Thiago_Senna
sergiotaborda:
O entrave fundamental que parece permear este tópico é dar exemplos de DDD usando persistência. Embora persistencia seja um dominio também, ele é muito especial. Se misturarmos as duas coisas vamos acabando resolvendo que a entidades podem ser ActiveRecords e que o Repositorio não serve para nada (exatamente o contrário do que é afirmado pela DDD e frase citada deixa bem claro)
Não só persistência, mas os CRUD's em especial complicam nossa vida. Tomara que este tópico não se transforme em ActiveRecord X Repositório. Este trecho que você argumentou deixou claro que DDD != Active Record! ;)
sergiotaborda:
Destaco a seguinte frase da citação
The domain objects, free of the responsibility of displaying themselves, storing themselves, managing application tasks, and so forth, can be focused on expressing the domain model.
Os objetos de domínio , livres da responsabilidade de se apresentarem a si próprios , guardar a si próprios, gerenciar tarefas da aplicação, e assim por diante, podem se focar em expressar o modelo do domínio.
Para mim esta frase é muito clara e significa que:
1) Objectos de dominio não podem ter funções que dependam ou se destinem a UI ou IO. ("displaying themselves")
2) Objetos de domínio não podem seguir o padrão ActiveRecord ("save themselves") , nem nada semelhante. A responsabilidade de guardar e reobter os objetos é exclusivamente do repositório (por isso ele existe).
3) Serviços de domínio (digo de dominio para diferenciar dos de aplicação e outros) não podem depender de tecnologias (" managing application tasks"). E por isso não devem ser implementados dentro de um servlet ou coisas semelhante.
Vou tentar exemplificar com código o que seria um uso adequado de repositório e entidade na camada de aplicação:
Resumindo: Em geral na camada de aplicação usamos o repository para operações do tipo CRUD (ou outras operações muito simples), no entanto, quando conveniente para isolar a lógica de negócio na entidade podemos acessar o repositorio de dentro da entidade. Estamos avançando? :mrgreen:
R
ronildobraga
Isso depende muito da interpretação de cada pessoa, eu particularmente acho que sim, mesmo que as vezes a gente entre em contradição eu espero que no final todos tenham entendido alguma coisa.
Porem algumas pessoas acham que isso aqui é tudo desentendimento e que so falamos besteira, e ainda escreve artigos http://blog.fragmental.com.br/2007/06/22/cuidado-com-domain-driven-design/ falando sobre mim com relação a esse post e depois tem a coragem de vir aqui e tb dar a sua propria opiniao como se esse fosse outro post e ainda ignora tudo que eu falo.
Como eu tinha dito, eu traduzo os texto nao so por educação, mas tb para estabeler um linguagem comum entre a gente assim como o proprio livro do DDD aconselha
sergiotaborda
Não só persistência, mas os CRUD’s em especial complicam nossa vida.(…)
Se entendermos que CRUD é uma operação de manutenção/população do repositorio, então CRUD é apenas uma camada em cima do repositório.
Mais ou menos assim
O objeto Query serve para isolar a pesquisa da tecnlogia de persistencia subjacente.
Este serviço é um objeto da camada de aplicação que fica acima da de dominio. O principal detalhe que falta aqui é a validação antes de guardar. A qual deveria , a principio , ser responsabilidade do dominio e podemos pensar que está no repositorio (só para simplificar)
Quanto a mim o código seria mais independente (não necessáriamene melhor) desta forma:
protectedModelAndViewonSubmit(Objectcommand)throwsServletException{
AlunoTOalunoTo=(AlunoTO)command; // purismo de não usar entidades na camada de apresentação//delegatetheinserttothePersistencelayerCRUDServices=...
s.store(aluno);returnnewModelAndView(getSuccessView(),"aluno",alunoTo);}
No fim, o repositorio usa um DAO interno para persistir as alterações.
Poderiamos penar que o façade de crud trabalha directamente sobre o DAO sem passar pelo repositorio. É como se a carga das instancias das entidades fosse “exterior” ao repositório. Assim poderiamos usar JPA em cima dos TO e deixar as entidades puras, sem referencia a nenhuma tecnologia. Por outro lado teriamos que encontrar forma de validar esses TO durante a pesistencia. Aqui, poderiamos entender que a validação fica em outras classes do dominio (que eu chamo de validadores, mas a DDD chama de Specification … e no fim não é bem a mesma coisa) - os validadores. Os validadores seriam chamados pela aplicação antes do .CRUD algo como
protectedModelAndViewonSubmit(Objectcommand)throwsServletException{
AlunoTOalunoTo=(AlunoTO)command; // purismo de não usar entidades na camada de apresentaçãoAlunoValidatorv=newAlunoValidator();ValidationResultr=v.validate(alunoTo);if(!r.isValid()){
//poderiamospassaror,quecontémasrazõesdainvalidaçãoreturnnewModelAndView(getInvalidView(),"aluno",alunoTo);}
//delegatetheinserttothePersistencelayerCRUDServices=...
s.store(aluno);returnnewModelAndView(getSuccessView(),"aluno",alunoTo);}
Quanto mais independente a camada de dominio for da camada de aplicação e ambas da de persistencia mais transformações serão necessárias e mais objetos serão necessários (como Query).
Isto é um bocado chato. No fim das contas poderiamos usar as entidades anotadas com JPA. Só que, isso violaria o purismo de DDD e é uma escolha de implementação. Entendido isso, o bom é ter independência total para diminuir o acoplamento das camadas, mas isso implica em uma infraestrutura maior.
O meu ponto é que não se deveria confundir o serviço de CRUD com o Repositório. Eles serão tão mais confundiveis quanto mais acopladas tiverem as camadas. Por exemplo, sem TO, as ppr entidades navegam entre as camadas. Se não ha TO não ha Assembler e portanto o papel do seriço CRUD é futil. Mas se fizermos isso estamos usando um pseudo-DDD. No frigir dos ovos isso pode ser suficiente para alguma aplicação, mas concerteza não é recomendado acoplar as camadas assim.