Dúvida em DDD, aonde ponho essa regra de negócio?

Pessoal,

Estou começando agora no DDD, e esse é meu primeiro projeto tentando usar os conceitos… Talvez a dúvida seja boba, mas, vamos ver! :slight_smile:

Bom, eu tenho a seguinte regra de negócios… Tenho TABELAS DE PREÇO que variam mensalmente, ou seja tenho a tabela1 do mês 01/2008, 02/2008, e assim por diante…

Minha dúvida é: Como devo carregar essa tabela? Se pensarmos em banco de dados, tenho 2 tabelas relacionadas… TabelaDePreco e ValoresDaTabela.

Minhas classes estão mais ou menos assim:

public interface TabelaRepository {
Tabela pegaPorId(int id) throws SQLException;
List pegaValores(int tabela, int mes, int ano) throws SQLException;
}

public class Tabela {
private String nome;
private List valores;
// getters and setters!

public Tabela() { valores =new LinkedList(); }

public void carregaValores(int mes, int ano) {
setValores(repositorio.pegaValores(getId(), mes, ano));
}
}

E aí no meu Struts Action, eu faço algo do tipo:

Tabela t = repositorioTabelas.pegaPorId(id);
t.carregaValores(01,2008); // para carregar janeiro!

Estou pensando corretamente?

O que vocês sugerem?

Acabou de me vir na cabeça, que o cliente da loja nunca abre uma tabela sem mês ou ano definido (afinal pra ele, tabelas sem mês ou ano não existem, correto?). Então minha busca de tabelas, além do ID, eu devo passar mes e ano obrigatoriamente?

Conseguiram entender? :wink:

Grato,
Mauricio

  1. Repositorios não lançam SQLException!
  2. Repositorios não são interfaces!

O que vc quer é trabalhar com dados que dependem da data. Isso é algo comum e para resolve isso vc cria um método passando a data.

Simplesmente faça assim

public class TabelaRepository {

  Tabela pegaPorId(int id) {
          
         //  executa SQL
         Tabela  t = ...
         t.valores = pegaValores(t, new Date()); // pega os mais atuais
         return t
           
  }

  List<ValorTabela> pegaValores(Tabela tabela,  Date date){
           // executa SQL

  }
}

public class Tabela {
  private String nome;
  List<ValorTabela> valores;

  // getters and setters!

  public Tabela() {  }

  private Tabela(Tabela other) {  
   // construtor de copia. 
  // copia todos os atributos da tabela other para uma nova tabela
  }

  public Tabela valueAt(Date date) {
    Tabela t =  new Tabela(this) ;
    t.valores = repositorio.pegaValores(this, date);
    return t; 
  }
}

Desta forma vc obtem a tabela com os preços atuais por default. Se em algum ponto vc precisa das tabelas anteriores vc pode fazer isso executando valueAt()
Use Date ou um outro objeto ( No seu caso um MonthOfYear seria melhor mas pode usar Date com o dia setado em 1). Isso aumenta a segurança do seu codigo e a legibilidade.

Oi Sérgio,

Eu coloquei meu Repositorio como interface, pois pelo que vi em todas as discussões aqui, um DAO deve implementar um Repositorio…

Então eu tenho um iBatisDAO que extende meu Repositório… Não usei injeção de dependência pq o projeto é muito simples, acabei usando um factoryzinho básico…

Entendi que não posso fazer ele lançar SQLException já que a exceção está ligada à um banco de dados… Mas posso fazer ele lançar um RepositorioException, certo? Afinal, falhas podem ocorrer, eu devo tratá-las!

Entendi sua abordagem, foi o que coloquei no final da minha msg!

Acrescentando mais uma dúvida:

Eu preciso deletar um item ali na lista de itens… É um projeto web, e você sabe que o máximo que eu posso fazer é passar o ID do item a ser excluido… Minha solução, veja se você concorda:

class Tabela {
public ItemTabela deletaItem(int id) {
for(ValorTabela it : getValores()) {
if(it.getId()==id) {
getValores().remove(it);
// exclui do repositorio
repositorio.deletaItem(it);
}
}
}
}

Repare que eu chamei a função de deletar do repositório dentro da função. Como o pcalcado disse em outros posts, e não me recordo exatamente das palavras, mas a idéia era que ter que pedir pro repositório excluir é uma ‘falha’, mas somos obrigados a excluir de alguma forma o item do banco de dados…

Vocês concordam com o código acima?

Desculpem pelas perguntas tão ignorantes, mas é só pondo a mão na massa que se aprende! :slight_smile:

[]'s
Mauricio

Um DAO deve abstrair em qual banco de dados vc guarda os dados, esta é a motivação do padrão. Ele não precisa necessariamente implementar um repositório, embora possa tbm ser implementado desta forma. Particularmente eu prefiro compor um Repositório com um DAO, do que implementa-lo com um.

Alessandro,

Então seu repositório é algo parecido com:

class Repositorio {
private Dao dao;

public Elemento pegaPorId(id) { return dao.pegaPorId(id); }
}

Você assim não acaba apenas repassando o comando pro teu DAO? Qual a vantagem de compor o repositório com o DAO?

[]'s
Mauricio

Qual a API uso para persistir ou consultar meus dados abaixo?

class RepositorioX{

	@In
	DAO dao;	

	public void adiciona(X objetoX){
	    dao.persist(x);	
	}

	public X buscaXPorNome(String nome){
		
		dao.query.buscar(X.class,new Restricao[]{"nome",nome})	
	}

}

[quote=MauricioAniche]Oi Sérgio,
Eu coloquei meu Repositorio como interface, pois pelo que vi em todas as discussões aqui, um DAO deve implementar um Repositorio…
[/quote]

hum… procurar no guj é legal, mas procure tb fora dele …

Repare na contradição logica : um DAO extend um Repositorio.
“Extend” significa “é um”. Vc está dizendo que “DAO é um Repositorio” … humm meio inutil ter dois nomes para a mesma coisa… Se é um DAO deve extender DAO , se é um Repositorio deve extender Repositorio

O que é mais logico


class iBatisDAO  extends Repository

// ou

class iBatisDAO  extends DAO

E como eu disse antes, Repositorio não é uma interface. É uma classe.
Vc faz um e usa sempre o mesmo
DAO é uma interface. Vc faz um, mas no futuro pode fazer outro e ter dois.

certo. Pode ser algo mais geral como PersistenceException.

cof cof… isso é minimo, não o máximo. Vc pode passar o objeto a ser excluido

Esse código está meio confuso. Porque eu gostaria de remover um Valor da tabela ?
E porque eu gostaria que isso se refletisse automáticamente no banco ? E se eu quiser me arrepender ?

uma opção

class Tabela {
  public ItemTabela remove(ItemTabela item) {
        return valores.remove(item);
  }
}

class DAO {

   public void altera (Tabela tabela){}

}

Oi Sérgio,

Eu não procuro apenas no guj… O GUJ aliás, comecei a ler há alguns dias quando um amigo me disse que haviam bastante pessoas de DDD discutindo lá! :slight_smile:

Eu usei a palavra errada. No meu caso meu DAO implementa Repositorio (já que ele é uma interface)… Mas entendi e concordo com vc!

Sim, é o mínimo (de novo me expressei mal), mas o usual é mesmo passar o id do elemento que você quer apagar. Não sei como fazer em Java, para que de uma forma fácil ele guarde o objeto e recupere na hora do post. Em .NET (na qual eu conheço um pouco mais) eu usaria ViewState ou até mesmo o framework MindLib para isso.

Pq eu apagaria o objeto naquele momento? Não sei, regra do cliente… A minha dúvida era se eu poderia chamar o repositório para operações de altera(), deleta(), etc, de dentro do domínio, do jeito que eu fiz!

[]'s
Mauricio

“poder” vc pode. “dever” vc não deve.
Alterações do estado do sistema devem estar dentro de uma transação , de um processo. Senão vira uma zona…

Sérgio,

Entendi, na teoria. Na prática, como implemento esse processo?

[]'s
Mauricio

[quote=MauricioAniche]Sérgio,

Entendi, na teoria. Na prática, como implemento esse processo?

[/quote]

Existem várias formas.

)chamar o DAO directamente (TabelaDAO.alterar(tabela)). Este é o mais usual. Parte-se do principio
de que quem executa este codigo está dentro de uma transação. Se não estiver usa-se outro método

) chamar um método em um serviço especial
(TabelaService.altera(tabela)) isso é normalmente usado quando a lateração da tabela tem outras impliações.
Por exemplo quando vc guarda uma conta bancária vc quer inicializar o saldo dela criando um registro em outra tabela. O facto de usar um serviço ( na realidade um método) vc pode injetar o controle transacional dentro dele ou (em ambiente JEE) em torno dele.

) seguir o padrão UnitOfWork. Aqui vc cria uma classe (unidade de trabalho) que tem um método execute() por exemplo. Vc inicializa essa classe com os parametros necessários ao funcionamento. No caso a tabela. Ai vc envia para um executor de trabalho (WorkExecutor). Esse objeto irá iniciar a transação, executar a sua unidade de trabalho e finalizar a transação. Em opção ele pode ainda controlar concorrencia ( não pode executar duas unidades de trabalho da mesma classe ao mesmo tempo). Exemplo

class TabelaUnitOfWork extends UnitOfWork
Tabela tabela;
public TabelaUnitOfWork(Tabela tabela){
  this.tabela = tabela;
}

public void execute(){

   TabelaDao dao = .. 
   dao.alterar(tabela);
}

Os dois ultimos mecanismo são especialmente utilizados onde ha mais coisas a fazer que simplesmente gravar no banco. Nesses casos utiliza-se o primeiro. Mas sempre tendo em atenção que o codigo é chamado dentro de uma transação.

Não sei se respondi a pergunta…

Cuidado com os “certos” e “errados”.

Não existe QUALQUER problema em um Repositório ser apenas uma interface se ele não possuir lógica. Uma interface é um contrato, implementar uma interface diz que o objeto implementa o contrato. Interfaces não são hierarquias.

Tirou palavras da minha boca. (essa thread pode ficar ferrenha)

Olá galera, cada vez que leio um tópico sobre DDD fico mais confuso, uns falam que pode ser uma interface caso não tem lógica de negócios nenhguma, o que concordo totalmente, mas dizem também que no máximo pode ser uma classe abstrata, mas nunca uma concreta. Mas de acordo com a definição do sergiotaborda Repository, seria uma classe concreta, então no Padrão Repository criado por MartinFowler o Repository seria o que afinal??

Att

Gostei desse modo, assim ainda tira a “preocupação” de como instânciar o repositório…

:smiley:

[quote=danielbussade] Olá galera, cada vez que leio um tópico sobre DDD fico mais confuso, uns falam que pode ser uma interface caso não tem lógica de negócios nenhguma, o que concordo totalmente, mas dizem também que no máximo pode ser uma classe abstrata, mas nunca uma concreta. Mas de acordo com a definição do sergiotaborda Repository, seria uma classe concreta, então no Padrão Repository criado por MartinFowler o Repository seria o que afinal??
[/quote]

Eu não defini nada. Eu segui a definição do Fowler ( http://www.martinfowler.com/eaaCatalog/repository.html )
Se a página não é suficiente, leia o livro. (lá tem assim “the repository” = “o repositorio” , “o” é diferente de “um”. Por outro lado “the repository” não chama nenhum “repository implementation”)

Como se sabe se algo tem que ser interface ou classe ? Algo é uma classe quando outros do mesmo tipo não possa ser outras classes. Ou seja, se a classe X é um Cliente ele não pode ser um Produto.
Se Cliente e Produto forem interfaces eu posso ter um X que é cliente e produto. Isso é um absurdo. Para evitar isso a boas práticas de modelagem ensinam a utilizar classe nesses casos ( para limitar a herança)
Utiliza-se interface quando algo pode ser aquela coisa mas tb outras. Por exemplo, Comparable , Serializable.
A classe X pode ser comparable E serializable, não ha obrigação em só um dos dois.

Isto é POO básico. Não é nenhuma filosofia esotérica…

Então sérgio porque o DAO poderia ser uma interface, mas o repository não??? Se eu digo que o DAO á uma interface eu estou dizendo qualquer classe pode ser DAO mas também ser outras coisas, como vc mesmo disse.

[quote=danielbussade]Então sérgio porque o DAO poderia ser uma interface, mas o repository não???
[/quote]

O objetivo do DAO é vc poder criar vários um para cada mecanismo de persistencia que vc quiser.Mas vc não sabe à partida como fará a integração com esse mecanismo. Portanto, em tese, vc pode precisar extender uma classe de um API de terceiros para poder criar seu DAO. Dai o DAO ser normalmente uma interface. É uma questão relacionada à implementação desse padrão em particular. O normal seria ser uma classe abstracta já que uma classe que é DAO não é mais coisas nennhuma (salvo a exceção que já expliquei).
No caso do repositório, ele contém regras de negocio. E uma classe repositório é isso mesmo, um repositório. Não é mais coisa alguma. Como é uma classe de domínio com certeza não precisa herdar de nenhuma classe especial de terceiros. Como ela contém regras de negocio não existirão dois repositórios seria como ter duas regras diferentes para a mesma coisa… é tudo uma questão de lógica…

Olá Sérgio agora sim entendi o que quis dizer,a única coisa que nao ficou clara em relação aos DAO’s foi o seguinte vc citou isso

Isso eu não entendi, se eu precisar extender de uma classe de terceiros eu nao vou conseguir sendo o DAo uma interface já que um interface só pode extender outra interface, mas sendo o DAO uma classe abstrata eu consegueria poderia me explicar melhor??

Outra coisa em outro post você citou que a ordem correta de uma requisição WEB até o banco seria a seguinte:

Poderia explicar melhor este padrão Façade/Service, e este Factory?? Pra que eles servem?? além disso porque num processo de listagem é diferente de save?

Desculpa pelas muitas dúvidas!!

Opa!!! Não é só porque criamos duas interfaces que estamos entao considerando que alguem pode implementa-las ao mesmo tempo. Set e List. Queue e Map. Comparable e Comparator. Faz sentido alguem implementar as duas? Meio estranho… mas vai saber. Pelo menos voce nao fechou portas, ou usou heranca, que seria bem pior… Se o cara diz que é uma Queue e um Map ao mesmo tempo, e realmente cumpre os dois contratos, bom pra ele…

Tem gente que vai falar que POO basico é voce programar sempre voltado a interface, mesmo no sentido Java da palavra. Muita gente é xiita e interfaceia simplesmente TODAS as classes. Pra ser sincero nao sou muito contra isso. Enfim, existem centenas de ideias, conceitos e praticas que algumas pessoas discordam, outras concordam.

Eu nao vejo problema algum em interfacear um repositorio, mas entendo o argumento do Sergio.