Les objets de domaine en tant qu'identités créent des problèmes complexes / subtils:
Sérialisation / désérialisation
Si vous stockez des objets sous forme de clés, la sérialisation du graphe d'objets sera extrêmement compliquée. Vous obtiendrez des stackoverflow
erreurs lors d'une sérialisation naïve vers JSON ou XML à cause de la récursivité. Vous devrez ensuite écrire un sérialiseur personnalisé qui convertit les objets réels pour utiliser leurs identifiants au lieu de sérialiser l'instance d'objet et de créer la récursivité.
Passez des objets pour la sécurité des types mais ne stockez que les ID, vous pouvez alors avoir une méthode d'accesseur qui charge paresseusement l'entité associée lors de son appel. La mise en cache de deuxième niveau prendra en charge les appels suivants.
Fuites de référence subtiles:
Si vous utilisez des objets de domaine dans des constructeurs comme vous en avez, vous créerez des références circulaires qui seront très difficiles à permettre à la mémoire d'être récupérée pour les objets qui ne sont pas activement utilisés.
Situation idéale:
Identifiants opaques vs int / long:
Un id
devrait être un identifiant complètement opaque qui ne porte aucune information sur ce qu'il identifie. Mais il devrait permettre de vérifier qu'il s'agit d'un identifiant valide dans son système.
Les types bruts brisent ceci:
int
, long
Et String
sont les types d'identifiants bruts les plus couramment utilisés dans le système SGBDR. Il existe une longue histoire de raisons pratiques qui remontent à des décennies et ce sont toutes des compromis qui s'inscrivent dans l'épargne space
ou l'épargne time
ou les deux.
Les identifiants séquentiels sont les pires contrevenants:
Lorsque vous utilisez un identifiant séquentiel, vous intégrez par défaut des informations sémantiques temporelles dans l'identifiant. Ce qui n'est pas mal tant qu'il n'est pas utilisé. Lorsque les gens commencent à écrire une logique métier qui trie ou filtre la qualité sémantique de l'identifiant, ils mettent en place un monde de douleur pour les futurs responsables.
String
les champs sont problématiques parce que les concepteurs naïfs intégreront des informations dans le contenu, généralement la sémantique temporelle également.
Il est donc impossible de créer un système de données distribué également, car il 12437379123
n'est pas unique au monde. Les chances qu'un autre nœud d'un système distribué crée un enregistrement avec le même numéro est à peu près garantie lorsque vous obtenez suffisamment de données dans un système.
Ensuite, les hacks commencent à travailler autour de lui et le tout se transforme en un tas de désordre fumant.
Ignorant les énormes systèmes distribués ( clusters ), cela devient un véritable cauchemar lorsque vous essayez également de partager les données avec d'autres systèmes. Surtout lorsque l'autre système n'est pas sous votre contrôle.
Vous vous retrouvez avec exactement le même problème, comment rendre votre identifiant globalement unique.
L'UUID a été créé et normalisé pour une raison:
UUID
peut souffrir de tous les problèmes énumérés ci-dessus en fonction de celui que Version
vous utilisez.
Version 1
utilise une adresse MAC et une heure pour créer un identifiant unique. C'est mauvais car il contient des informations sémantiques sur l'emplacement et l'heure. Ce n'est pas en soi un problème, c'est lorsque des développeurs naïfs commencent à s'appuyer sur ces informations pour la logique métier. Cela fuit également des informations qui pourraient être exploitées lors de toute tentative d'intrusion.
Version 2
utilise un utilisateur UID
ou GID
et domian UID
ou GUI
à la place du temps à partir de Version 1
cela est tout aussi mauvais que Version 1
pour les fuites de données et risquer que ces informations soient utilisées dans la logique métier.
Version 3
est similaire mais remplace l'adresse MAC et l'heure par un MD5
hachage d'un tableau de byte[]
quelque chose qui a définitivement une signification sémantique. Il n'y a aucune fuite de données à craindre, le byte[]
ne peut pas être récupéré à partir du UUID
. Cela vous donne un bon moyen de créer de manière déterministe des UUID
instances de formulaire et une clé externe quelconque.
Version 4
est basé uniquement sur des nombres aléatoires, ce qui est une bonne solution, il ne contient absolument aucune information sémantique, mais il n'est pas recréable de façon déterministe.
Version 5
est comme Version 4
mais utilise sha1
au lieu de md5
.
Clés de domaine et clés de données transactionnelles
Ma préférence pour les identifiants d'objet de domaine est d'utiliser Version 5
ou Version 3
si l' utilisation est restreinte Version 5
pour une raison technique.
Version 3
est idéal pour les données de transaction qui peuvent être réparties sur de nombreuses machines.
Sauf si vous êtes contraint par l'espace, utilisez un UUID:
Ils sont garantis uniques, en vidant les données d'une base de données et en les rechargeant dans une autre, vous n'avez jamais eu à vous soucier des identifiants en double qui référencent réellement différentes données de domaine.
Version 3,4,5
sont complètement opaques et c'est ainsi qu'ils devraient être.
Vous pouvez avoir une seule colonne comme clé primaire avec un UUID
, puis vous pouvez avoir des index uniques composés pour ce qui aurait été une clé primaire composite naturelle.
Le stockage ne doit pas nécessairement l'être CHAR(36)
non plus. Vous pouvez stocker le UUID
dans un champ natif octet / bit / numéro pour une base de données donnée tant qu'il est toujours indexable.
Héritage
Si vous avez des types bruts et que vous ne pouvez pas les changer, vous pouvez toujours les résumer dans votre code.
L'utilisation de l'un Version 3/5
d'entre UUID
vous peut passer le Class.getName()
+ en String.valueOf(int)
tant que a byte[]
et avoir une clé de référence opaque qui est récréative et déterministe.