Consegui evitar os VOs e BOs?

[quote=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…

[/quote]
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.

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.

So retratando um exemplo rapido para corrigir o primeiro exemplo que eu postei

//Meu servlet ou algo do genero
public class Beta {

	private void usandoDDDCorretamente(){
		LogonTO to = new LogonTO();
		to.setName("fulano");
		to.setKey(1);

		to = new UserService().saveUser(to);
		System.out.println(to.getName());
	}
}

//TO, VO, DTO novamente chamem como quiser
class LogonTO{
	private String name;
	private int key = 50;	
	... geters and seters
}

//Entidade do dominio
class PersonEntity{
	private int key = 50;
	... geters and seters
}

//Entidade do dominio
class LoginEntity{
	private String name;
	... geters and seters
}

//Servico do dominio, contem a logica de negocio do dominio
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()); 
	}
}

//Meio de como persistir os dados do dominio
class Repositorio{
	public static void save(LoginEntity login, PersonEntity person){
		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

[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.

uahuahuahuauha :lol:

PS: Tô por fora dessa thread, mas esse comentário foi hilário hahaha

fiz algo de errado :shock: ?

fiz algo de errado :shock: ?[/quote]

Eu confundio você com o sergiotaborda no ultimo post. Foi mal, já alterei o post original.

fiz algo de errado :shock: ?[/quote]

Eu confundio você com o sergiotaborda no ultimo post. Foi mal, já alterei o post original.[/quote]
Nao quis dizer se o codigo esta errado, queria saber fiz alguma coisa de errado.

ele queria ter dado uma alfinetada no outro maluco, só que trocou os nomes.

auhauhah, mas to rindo até agora huahuahuh

[quote=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.[/quote]

Com uma frase vc justificou tudo o que eu disse. Obrigado.

http://pt.wikipedia.org/wiki/Argumentum_ad_hominem

Mas pra que o TO?

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.

Abraços!

Segue um exemplo melhorzinho:

Alguns métodos do Service:

[code]public Collection findOwners(String lastName) {
return Owner.findLikeLastName(lastName);
}

public Owner loadOwner(Long id) {
	return Owner.load(id);
}

public Pet loadPet(Long id) {
	return Pet.load(id);
}

public void storeOwner(Owner owner) {
	owner.save();
}[/code]

Uma entidade:

[code]@Entity
public class Visit extends BaseEntity {

private Date date;

private String description;

@ManyToOne
@JoinColumn
private Pet pet;


public Visit() {
	this.date = new Date();
}

            // getters and setters omitidos ...


public void save() {
	new VisitRepository().store(this);
}

public static Visit valueOf(Date date, String description) {
	Visit visit = new Visit();
	visit.setDate(date);
	visit.setDescription(description);
	return visit;
}

}[/code]

Um pouco de repositorio:

[code]public class VisitRepository extends Repository<Visit, Long> {

public VisitRepository() {
	super(Visit.class);
}

}[/code]

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. :slight_smile:

Até +

…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.

So corrigindo um detalhe no seu 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

[quote=Rafaelprp]ele queria ter dado uma alfinetada no outro maluco, só que trocou os nomes.

auhauhah, mas to rindo até agora huahuahuh[/quote]
: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.

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!


class PessoaVO {
    int idade;
}

class PessoaBO {
    int getDiferencaIdade(PessoaVO _p1, PessoaVO _p2){
        return _p1.idade - _p2.idade;
    }
}

Um modelo é dito bem nutrido quando eu tenho uma classe que possui dados e ela mesmo manipula esses dados!


class Pessoa {
    int idade;

    int getDiferencaIdade(PessoaVO _p){
        return this.idade - _p.idade;
    }
}

Ao meu ver é isso!

Espero ter ajudado ou se eu estiver errado alguem me corrija!

[]s

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:



 class UserService {

 public void createLoginFor (String username, byte[] password) {
        // estou pedindo dados em bruto pq este´e um serviço do dominio
        // será chamado pelo servlet facilmente sem definir TO
       
        Person p = Repositoy.find(Person.class, username); // username é unico, então atua como chave 

        if (p==null) { 
            // essa pessoa não existe
            throw new PersonNotFoundException(username);
        }

        PersonLogin login = 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) 
 public void autenticate (String username , byte[] password ) {

        Person p = Repositoy.find(Person.class, username); // username é unico, então atua como chave 

        if (p==null) { 
            // essa pessoa não existe
            throw new IncorrectUserCredentialsException(username);
        }

        PersonLogin login = PersonLogin.valueFor (p); 

        if (!login.accept(password)){
             throw new IncorrectUserCredentialsException(username);
        }

 // se está tudo ok, continua. não precisa retornar nada.
 }
}

@Entity  // o uso desta anotação , introduzido pelo Thiago Senna é uma
 boa, mas eu não usaria a anotação de JPA (persistencia) e sim uma 
anotação puramente de dominio. Mas isso ai é gosto e implementação, 
por agora entenda-se que @Entity significa 
apenas "isto é um entidade de dominio"
class PersonLogin {

   private Key key;   // Key é um VO
   private Person p
   private byte[] password;

   public PersonLogin valueFor (Person p) {
        PersonLogin  login =  Repository,find ( PersonLogin.class, p.getUsername())

        if (login == null){
          login = new PersonLogin (p , new byte[0]);
      }
       
return login;
   }

   private PersonLogin (Person p, byte[] password){
       // atribuções 
   }


 public boolean accept( byte[] password) {
    return Arrays.equals(this.password, password);
 }

}

A seguir o codigo a que se refere este post.

[quote=ronildobraga]So retratando um exemplo rapido para corrigir o primeiro exemplo que eu postei

//Meu servlet ou algo do genero
public class Beta {

	private void usandoDDDCorretamente(){
		LogonTO to = new LogonTO();
		to.setName("fulano");
		to.setKey(1);

		to = new UserService().saveUser(to);
		System.out.println(to.getName());
	}
}



//Servico do dominio, contem a logica de negocio do dominio
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()); 
	}
}

//Meio de como persistir os dados do dominio
class Repositorio{
	public static void save(LoginEntity login, PersonEntity person){
		System.out.println("gravando: " + login.getName() + " chave: " + person.getKey() );
	}
}

[/quote]

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 …

@Entity 
class Person {
 
   DayOfYear dataNascimento; // isto é um VO que contém ano, mes e dia
   // outros atributos

   // get/set 

   // retorna a idade que a pessoa teria/terá na data passada
   public int age(DayOfYear data) {
         return dataNascimento.to(date).duration().years();
 // mesmo sem saber o codigo de DayOfYear ler a linha acima é simples
 // esse é um objetivo colateral de DDD
   }

   public boolean olderThan (Person other) {
       // basta comparar as idades hoje. 
       // a relação se mantém para qq ano
       return this.idade(DayOfYear.today()) > other.idade(DayOfYear.today()); 
   }

    public boolean hasBorn(DayOfYear data){
          return this.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)

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.

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.

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).
[/quote]

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.