Persistência de um Value Object (DDD) na prática

[quote=pafuncio]
Cara não sei se você já chegou à um entendimento, mas a questão é a mistura que você está fazendo entre a persistência e o domínio. No seu domínio, o objeto XPTO vai ser um Value Object, porém, do ponto de vista da persistência, você vai precisar de um Id (considerando esse caso), mas note bem, o ID é necessário apenas do ponto de vista da persistência, não do ponto de vista do seu domínio, logo, para o seu domínio, ele não é um Entity.[/quote]

Chamemos Key ao identificador de persistência. Pode ser um inteiro qq (ex : autonumber).
Chamemos ID à propriedade que traduz a Identidade da Entidade no sistema. Normalmente é composta de um ou mais vários campos de dados (ex. cpf).

Se existe um ID é porque é uma entidade.
VO significa que pode ser intercambiado entre duas entidades.

O exemplo do endereço.

Se E1 é o endereço da pessoa A e E2 é o endereço de B, se A e B têm no mesmo endereço então E1.equals(E2)
A e B não são ubiquos portanto têm 1 só endereço (OneToOne). Se eu fizer A.setAddress(B.getAddress())
estou dizendo que A têm agora o mesmo endereço de B.

Ao guardar no banco a tabela Pessoa tem:

a) os campos do endereço. Desta forma o objeto endereço é mapeado para campos da tabela pessoa. Isto representa implicitamente que o endereço é um VO de Pessoa

b) fazer o mesmo que (a) mas usando 2 tabelas. Pessoa tem um campo apontando o key do endereço
O endereço têm o key dele e uma foreign-key apontando pessoa (OneToOne).

Se o endereço não tiver uma foreign-key apontando pessoa será uma relação ManyToOne que viola o principio do VO. Não dá para reaproveitar o mesmo registro para E1 e E2 mesmo se eles são iguais ( se fossem entidades não so poderia como deveria).

Isto nos conduz a saber que A.setAddress() deve copiar o endereço de B para o seu e não apenas substituir a referencia.


class Pessoa

setAddress(Address a){
    this.address.copyFrom(a); // isto porque o key de address não muda, só os dados
}

Esta necessidade de usar a copia indica que o endereço com duas tabelas não é realmente um VO pois não é independente de quem o usa. (Se usar só a tabela pessoa, ñ tem problema nenhum). Ou seja, o E1 e o E2 não comutáveis. Logo, isto indica que o objeto endereço
pertence à pessoa com um vinculo muito maior. Isto faz pessoa não ser apenas uma entity, mas tb um agregado.
( ou seja, um grafo em que a raiz controla os outros nodos)

c) Aqui a tabela endereço não tem key e apenas uma foreign-key para pessoa.
Pessoa não tem key apontando endereço, mas tb não precisa. Esta forma é mais saudável e semelhante a (a) pois não obriga a fazer cópias. contudo esta opção de banco identifica um OneToMany e pode dar errado se não for bem implementado.

Uma pessoa não pode ter vários endereços, já que a pessoa não existem em vários lugares ao mesmo tempo.
O que a pessoa pode ter são vários endereços classificados por finalidade (moradia,entrega, faturamento, matriz, etc…)
Neste caso existe uma classificação da relação e o modelo terá que ser: uma pessoa tem vários EndereçosClassificados. Cada EndereçoClassificado tem um endereço e uma classificação.

Olá

Tópico muito bom. Só vou dar meu pitaco para reforçar o que o Sérgio escreveu de vários modos diferentes: CPF não é um identificador único.

A própria Receita Federal diz isto. Uma vez reclamei com a receita sobre um japonês safado que estava usado meu CPF de forma fraudulenta e a resposta da Receita foi de que no Brasil 30% (este foi o número dito pela auditora fiscal) dos CPFs estão errados ou são falsos. A maior parte dos erros está nos CPFs antigos em que o número era datilografado no cartão e o datilógrafo errava sem que o cidadão tivesse condições de suspeitar disto.

Portanto, nunca confiem apenas no número de CPF.

[]s
Luca

Sergio,

Obrigado pelos esclarecimentos, Eu realmente achei que a questão de ter um identificador do banco ia influenciar na minha classe tornando-a um Entity. Porém a sua explicação foi perfeita. Mais uma vez obrigado.

Luca,

Quanto ao CPF, eu concordo com o que você e o Sergio disseram. Acho que não me expressei direito.

O que eu quiz dizer é que em muitos sistemas vemos o CPF como o campo identificador de uma pessoa. Seria então errado uma classe Pessoa por exemplo com o atributo CPF sendo o seu ID em um determinado domínio que nada tem a ver com essas fraudes etc etc etc. Se não, o correto é ter um Número auto incremento para identificar essa Pessoa ? Nesse caso qual a melhor escolha para o ID desse Entity ?

Abs

[quote=emerleite] Se não, o correto é ter um Número auto incremento para identificar essa Pessoa ? Nesse caso qual a melhor escolha para o ID desse Entity ?
Abs[/quote]

Essa é uma boa pergunta. Nos Estados Unidos essa identificação é feita através do numero do seguro social.

Mas isso tambem depende do seu dominio, por exemplo, se voce vai modelar um sistema que Pessoa tambem pode ser uma crianca, CPF ja nao eh mais valido.

Em muitos dos domínios, um auto-incremento vale. Para alguns mais preciosistas, uma PK composta por CPF e um upperCase do nome. Não sei o que os mais experientes (Luca e Philip e demais) sugerem.

Olá

Para mim seria um campo ID incrementado dentro do sistema com a creta absoluta de que não há duplicidades.

E como eu disse, há CPFs duplicados por erro de digitação do cartão sem que haja fraude.

[]s
Luca

[quote=emerleite]
Nesse caso qual a melhor escolha para o ID desse Entity ?
Abs[/quote]

A identidade da entidade é uma propriedade dela que pode ser manifestada no sistema como um VO da entidade.
Vc pode ter um VO EntityIdentity que deve obdecer o contrado de equals e hashCode e deve poder ser um identificador da entidade num Map. Isto significa que esse objeto tem que ser unico para cada entidade.

Em tese não importa a estrutura interna desse objeto desde que ele obdece ao contrado de equals/hashcode
Então vc pode ter um PersistenteKeyEntityIdentity que usa o key do registro ( um auto-number da vida) para ser o identificador. Vc pode ter um EntityFieldsEntityIdentity em que vc escolhe alguns campos da entidade para serem identificadores em conjunto (chave composta). Aqui vc pode usar o cpf o nome , ou qq coisa que vc quiser, já que fica encapculado no EntityIdentity. Vc pode usar o EntityIdentity para procurar pela entidade. exemplo

EntityIdentity  id = PessoaEntityFieldsEntityIdentity.from (cpf, nome);

Pessoa p = manager.find(Pessoa.class,id);

Internamente o manager saberá como usar EntityIdentity para procurar por pessoa.

Mas tem um outro detalhe. Vc tem que assegurar que só existe uma pessoa para cada EntityIdentity.
Neste caso ao introduzir uma nova pessoa no sistema ele deveria testar isso. Para fazer isto o EntityIdentity não pode ser baseado no PersistenteKey já que esse numero sempre será diferente. Exemplo:

[code]
public void store ( Pessoa p ){

// p ainda não tem chae de persistencia
EntityIdentity id = PessoaEntityFieldsEntityIdentity.from §;
// verifica se existe uma pessoa com a mesma identidade
// a pessoa sempre tem um identidade
if ( manager.exists(id) ){
// se existe esta sendo tentada uma duplicação
throw new EntityDuplicationException();
}
// se não existe, guarda.
manager.insert§;
// agora p tem chave de persistencia

}[/code]

Existe portanto dois conceitos, a chave de persistência, que serve para controlar o estado e a referencia do objeto persistido e a chave de identidade que serve para identificar a entidade.