[quote=sergiotaborda][quote=ronildobraga][quote=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.
[/quote]
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.
.[/quote]
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 (Conta a , Conta b , Dinheiro m ) throws TransferException{
JTATransaction T = ...
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 Conta
throw TransferException("Salso insuficiente");
}
Transferencia t = new Transferencia (a,b,m , Data.hoje());
t.executa();
Repository.store(t);
T.commit();
} catch (TransferException e) {
T.rollback();
throw e;
}
}
Tranferencia {
public void executa (){
// tira de a
MovimentoConta ma = new MovimentoConta(a, m.times(-1) ,b , data);
// da a b
MovimentoConta mb = new MovimentoConta(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. [/quote]
É 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