Eu li boa parte do livro do Jimmy Nilsson e uma parte do livro do Eric Evans com certa dificuldade pq nao sou um expert no inglês oq talvez tenha prejudicado um pouco minha interpreetação.
Enfim qdo falamos em DDD falamos em Dominio, mas td faz parte do Dominio:
Entites
Services
Repositories “no meu entendimento a interface”, pois a implementação fica na Infrastrutura
Sabendo disso em qual camada seria adequado eu verificar as regras da minha aplicação? Algo como:
Um pedido nao pode ser maior q 1 milhão
Um cliente nao pode gastar mais q seu limite de credito no site
Os usuarios não administradores não podem excluir itens
E outras regras deste tipo
E uma outra coisa é como eu devolvo a informação para a UI? Pq em uma requisição eu posso infringir mais de uma regra e a UI precisa informar isto ao usuário.
Tenho certeza dq nao existe uma formula magica, pois o DDD me pareceu mto abstrado. Mais alguns de vcs ja implementarao suas solucoes usando os conceitos do DDD e eu gostaria da opnião de vcs e a partir dai posso tirar minhas proprias conclusoes.
Há controvérsias sobre isso. Aqui no GUJ há basicamente duas correntes, uma a favor e outra contra manter a implementação do repositório na infraestrutura. De qualquer maneira, não achei nada no livro do Eric que falasse explicitamente o que é melhor ou não.
Na camada de negócios ora!
Exceções! Quando uma regra é desobedecida, uma exceção deve ser propagada para as camadas superiores até chegar na UI, onde recebe o tratamento necessário.
Eu li boa parte do livro do Jimmy Nilsson e uma parte do livro do Eric Evans com certa dificuldade pq nao sou um expert no inglês oq talvez tenha prejudicado um pouco minha interpreetação.
[/quote]
Ja existe a versao traduzida do livro do Evans, ainda nao li, nao posso falar sobre a qualidade da traducao, mas se voce apanha um pouco do ingles talvez valha a pena.
Entao, DDD nao redefine as velhas praticas conhecidas de responsabilidades dos objetos, ele só apresenta alguns padroes facilitando o uso das velhas regras de OO. A resposta a todas essas duvidas é a mesma. A regra deve ser implementada por quem possui as informacoes necessarias para implementa-las. Uma provavel classe Pedido é, num primeiro momento, o principal candidato a implementar a validacao do limite do pedido, ja que é ela provavelmente sabe seu proprio valor.
Mas o importante é entender o conceito sobre responsabilidade dos objetos, porque é comum errarmos e termos que refatorar, passando a responsabilidade para outro objeto. Uma das formas de identificar que a regra esta implementada no lugar errado é quando voce esta usando num objeto X muitos atributos do objeto Y, talvez entao essa regra deveria estar no proprio objeto Y.
Mas se sua pergunta era mais simples, a resposta é, sempre nas entities, nunca nos services ou repositorios.
ha opinioes divergentes, mas eu prefiro excecoes a ficar devolvendo codigos de erro.
Na verdade a formula magica esta nos fundamentos do orientacao a objetos. Talvez voce esteja pulando algumas etapas nos seus estudos.
Muito obrigado pelas respostas da galera.
Bom, o lance é que eu estava na dúvida de onde deveria verificar as regras e não onde devia implementa-las, ou seja, que a regra deve ser implementada dentro da classe pedido isso estava claro pra mim eu só tinha a dúvida do seguinte. Será q é realmente na camada Application que eu devo ter algo como:
public void MeuMetodoX(Pedido pedido)
{
if(pedido.Accept())
//true então mando para infra para persistir
else
//false disparo uma exception
}
ou se esta verificação normalmente fica dentro da implementação do Repositorio? Como falei eu não quero a formula mais sim as opniões do tipo:
- Eu fulano qdo tenho q fazer algo coloco na camada x
- Eu sicrano qdo tenho um problema deste tipo faço deste outro jeito
E assim eu consigo concluir se minha interpreetacao dos livros esta correta.
Não entendi bem sua dúvida mas vou postar um exemplo simples aqui pra clarear as idéias.
Vamos supor que você está desenvolvendo um programa de transporte de cargas. Há um requisito que diz o seguinte:
Baseado nesse requisito temos o código abaixo:
[code]public class Veiculo {
// Definição de atributos e métodos.
public boid carregar(Carga carga) {
if (carga.peso() > this.capacidade) {
// Exceção que herda de RuntimeException
throw new EuNaoAguentoTransportarIssoException();
}
// Associa a carga ao veículo.
}
}[/code]Veja que essa regra foi implementada na camada de domínio, e a exceção resultante de sua violação será propagada para as camadas superiores. O que eu costumo fazer é tratar essas exceções na camada de apresentação, exibindo uma mensagem ao usuário correspondente à regra que foi violada. Essa abordagem é bastante vantajosa se você estiver usando um framework transacional, como Spring ou EJB, pois a transação onde ocorreu o erro sofrerá rollback sempre que uma exceção for lançada.
[quote=diego.dfsd]Muito obrigado pelas respostas da galera.
Bom, o lance é que eu estava na dúvida de onde deveria verificar as regras e não onde devia implementa-las, ou seja, que a regra deve ser implementada dentro da classe pedido isso estava claro pra mim eu só tinha a dúvida do seguinte. Será q é realmente na camada Application que eu devo ter algo como:
public void MeuMetodoX(Pedido pedido)
{
if(pedido.Accept())
//true então mando para infra para persistir
else
//false disparo uma exception
}
ou se esta verificação normalmente fica dentro da implementação do Repositorio? Como falei eu não quero a formula mais sim as opniões do tipo:
- Eu fulano qdo tenho q fazer algo coloco na camada x
- Eu sicrano qdo tenho um problema deste tipo faço deste outro jeito
E assim eu consigo concluir se minha interpreetacao dos livros esta correta.
[]'s
[/quote]
Acho que entendi sua duvida agora. As regras sao implementadas nas entities, a sua duvida é onde elas sao chamadas, correto? Normalmente nos services.
public class ServiceProcessamentoDePedido{
public void processarPedido(Pedido p){
if (p.estaAcimaDoLimitePermitido())
throw new AcimaDoLimitePermitidoException();
//continua
}
}
Acho que era isso que voce estava perguntando, certo? Esse foi um exemplo bem simples para verificar a consistencia do pedido. Mas eu prefiro (e foi o que o tnaires sugeriu no post dele) nao deixar o pedido ficar inconsistente, lancando a excecao no momento em que o usuario tenta inserir um item que o valor ultrapasse o limite.
try{
pedido.adicionarItem(item);
}
catch(AcimaDoLimitePermitidoException e){
//informa o usuario
}
Onde esse metodo vai ficar depende da complexidade dele, se for muito simples eu deixo ele no managed-bean/action/qualquer-framework mesmo. Se for mais complexo, com varias acoes dentro deles ponho num service.
Poxa, muito obrigado pela ajuda. Todas as respostas foram muito bacanas, tão bacanas que acabei arrumando outras duvidas…rs
Vamos lá,
Se na infraestrutura devemos colocar o acesso ao banco e classes de suporte para as demais camadas, como por exemplo, leitura de arquivos e envio de e-mails eu gostaria de saber se seria errado dizer que:
Em uma situação onde um usuário faz upload de um arquivo e eu preciso ler este arquivo, e a partir do seu conteúdo carregar uma entity. Poderia criar no repositorio um método que usa um Stream e cria uma nova instancia da minha entity?
A outra situação é semelhante, vamos supor q a partir de um arquivo que o usuario fez upload eu precise carregar meu banco de dados, seria legal criar um service para salvar os dados usando as ferramentas da intraestrutura e depois carregar o repositorio responsavel por persistir a entity em questao e chamar um método add que recebe um stream para ser persistido ou poderia chamar um metodo no repositorio que carregar a entity a partir do stream e passar esta entity para ser persistida.
Talvez sejam duvidas bobas mais é complicado a mudança na forma do pensamento…
Realmente não sei o que é melhor. O repositório não deve transparecer para quem o utiliza que há uma fonte de dados por trás; ele deve fazer de conta que os objetos estão ali, em memória, como em uma collection. Nesse contexto, seria ruim ter um método como getFromStream(), pois detalhes de implementação estão vazando para o código cliente. Então deveríamos ter uma implementação do repositório dessa entidade que a recuperasse a partir de um arquivo já conhecido. Porém, o arquivo vem “de fora pra dentro”, isto é, ele precisa ser injetado em algum lugar para que seja convertido em uma entidade.
Dito tudo isso, IMHO talvez fosse melhor deixar essa conversão para a factory da entidade ao invés do repositório. Então ela usaria alguma classe da infraestrutura - um DAO talvez - para realizar a operação.
Já comentei sobre isso, talvez não fosse legal ter um método no repositório que recebesse um stream, pois detalhes de como o repositório realiza sua tarefa estão vazando para o código cliente. Já que os dados serão transferidos do arquivo para o banco diretamente, talvez esse processo não devesse passar pelo domínio. Talvez você devesse usar as classes de infraestrutura diretamente para realizar a importação.
P.S. - se falei alguma besteira, por favor peguem leve! Viu Sérgio Taborda? :lol:
[quote=diego.dfsd]Poxa, muito obrigado pela ajuda. Todas as respostas foram muito bacanas, tão bacanas que acabei arrumando outras duvidas…rs
Vamos lá,
Se na infraestrutura devemos colocar o acesso ao banco e classes de suporte para as demais camadas, como por exemplo, leitura de arquivos e envio de e-mails eu gostaria de saber se seria errado dizer que:
Em uma situação onde um usuário faz upload de um arquivo e eu preciso ler este arquivo, e a partir do seu conteúdo carregar uma entity. Poderia criar no repositorio um método que usa um Stream e cria uma nova instancia da minha entity?
[/quote]
Vc cria uma entidade a partir do stream ? Não. vc cria a partir do conteudo do stream. Isso significa que vc precisa ler o stream - de uma forma de depende do formato do arquivo - para determinar o conteudo e dai a entidade. Repositorios são feitos para procurar instancias da entidade no dominio. Um arquivo externo que chega no sistema não é do dominio. Aqui estamos no andar de integração e portanto fora do ambito e alcance do repositorio.
Vc simplesmente cria uma outra classe que le o strem e produz a entidade . ex:
class EntityReader<E> {
public static <T> EntityReader<T> getInstance(Class<T> type){
return new EntityReader(type);
}
private EntityReader (Class<T> type){
// guarda type num atributo
}
public EntityReader<E> setSource(InputStream stream){
this.sourceStream = stream;
return this;
}
public E read(){
if ( this.sourceStream == null){
throw new IllegalStateException("Please set a source first");
}
// lê entidade do stream.
// retorna entidade.
}
}
Um smile para quem souber que design pattern é este.
Uso
Se vc vai usar isto muito vc pode usar um outro objeto
public EntityImporter<E>{
public EntityImporter(Repository<E> repo , EntityReader<E> reader){
//guarda em atributos.
}
public void importAll(){
E obj = reader.read();
repo.save(obj);
}
}
Para o caso do arquivo ter mais do que uma instancia basta retornar uma coleção de objetos e ter um método save que aceita
uma coleção de objetos (aliás é sempre boa ideia ter isto porque se tira vantagem do tratamento batch).
Um smile para quem souber que design pattern o importer implementa.
Uso:
EntityImporter<Cliente> importer = new EnttiyImport(repositorio, EntityReader.getInstance(Cliente.class)
.setSource(new FileInputStream(new File("c:/arquivo.ext"))));
importer.importAll();
Sim, o primeiro é builder. um para vc.
O segundo não é decorator. (Ele usa dois objetos, repositorio e reader. os contratos não têm nada a haver. não é decorator)