Po kilku przydatnych odpowiedziach na pytanie, czy powinienem użyć obiektu domeny lub unikalnego identyfikatora jako parametru metody / funkcji tutaj Identyfikator vs. obiekt domeny jako parametru metody , mam podobne pytanie dotyczące: członkowie (w poprzednich dyskusjach nie udało się zakryj to). Jakie są zalety i wady używania unikalnych identyfikatorów jako członka vs. obiekt jako członek. Pytam w odniesieniu do mocno pisanych języków, takich jak Scala / C # / Java. Powinienem mieć (1)
User( id: Int, CurrentlyReadingBooksId: List[Int])
Book( id: Int, LoanedToId: Int )
lub (2), preferowane (1) Po przejściu: Czy powinniśmy definiować typy dla wszystkiego?
User( id: UserId, CurrentlyReadingBooksId: List[ BookId] )
Book( id: BookId, LoanedToId: UserId )
lub (3)
User( id: Int, CurrentlyReadingBooks: List[Book])
Book( id: Int, LoanedTo: User)
Chociaż nie mogę wymyślić korzyści z posiadania obiektu (3), jedną z korzyści posiadania identyfikatorów (2) i (1) jest to, że kiedy tworzę obiekt użytkownika z bazy danych, nie muszę tworzyć obiektu Book, który może z kolei zależeć od samego obiektu użytkownika, tworząc nieskończony łańcuch. Czy istnieje ogólne rozwiązanie tego problemu dla RDBMS i No-SQL (jeśli są różne)?
Na podstawie niektórych dotychczasowych odpowiedzi, przeformułowując moje pytanie: (przy użyciu identyfikatorów, które powinny znajdować się w opakowanych typach) 1) Czy zawsze używasz identyfikatorów? 2) Zawsze używasz obiektów? 3) Używać identyfikatorów, gdy istnieje ryzyko rekurencji podczas serializacji i deserializacji, ale używać obiektów w inny sposób? 4) Coś jeszcze?
EDYCJA: Jeśli odpowiesz, że Obiekty powinny być używane zawsze lub w niektórych przypadkach, upewnij się, że odpowiedziałeś na największe obawy, które opublikowali inni odpowiadający => Jak uzyskać dane z DB
źródło
Odpowiedzi:
Obiekty Domeny jako identyfikatory tworzą pewne złożone / subtelne problemy:
Serializacja / deserializacja
Przechowywanie obiektów jako kluczy spowoduje, że serializacja wykresu obiektów będzie wyjątkowo skomplikowana.
stackoverflow
Podczas wykonywania naiwnej serializacji do JSON lub XML wystąpią błędy z powodu rekurencji. Będziesz wtedy musiał napisać niestandardowy serializator, który konwertuje rzeczywiste obiekty w celu użycia ich identyfikatorów zamiast serializacji instancji obiektu i utworzenia rekurencji.Przekaż obiekty dla bezpieczeństwa typu, ale przechowuj tylko identyfikatory, wtedy możesz mieć metodę akcesora, która leniwie ładuje powiązaną jednostkę, gdy zostanie wywołana. Buforowanie drugiego poziomu zajmie się kolejnymi połączeniami.
Subtelne wycieki referencyjne:
Jeśli użyjesz obiektów domeny w konstruktorach takich, jakie masz, utworzysz odwołania cykliczne, które będą bardzo trudne do odzyskania pamięci dla obiektów nieużywanych aktywnie.
Idealna sytuacja:
Nieprzezroczyste identyfikatory vs int / long:
id
Powinna być całkowicie nieprzezroczysta identyfikatorem, który niesie żadnej informacji o tym, co go identyfikuje. Ale powinien oferować pewną weryfikację, czy jest to prawidłowy identyfikator w swoim systemie.Surowe typy łamią to:
int
,long
iString
są najczęściej używanymi typami surowymi dla identyfikatorów w systemie RDBMS. Istnieje długa historia praktycznych powodów, które sięgają dziesięcioleci i wszystkie są kompromisami, które albo pasują do oszczędzania,space
albo do oszczędzania,time
albo do obu.Identyfikatory sekwencyjne są najgorszymi przestępcami:
Podczas używania identyfikatora sekwencyjnego domyślnie pakowane są do niego informacje semantyczne. Co nie jest złe, dopóki nie zostanie użyte. Kiedy ludzie zaczynają pisać logikę biznesową, która sortuje lub filtruje semantyczną jakość identyfikatora, tworzą świat bólu dla przyszłych opiekunów.
String
pola są problematyczne, ponieważ naiwni projektanci umieszczają informacje w treści, zwykle także w semantyce czasowej.To sprawia, że niemożliwe jest również stworzenie rozproszonego systemu danych, ponieważ nie
12437379123
jest on unikalny na całym świecie. Szanse na to, że inny węzeł w systemie rozproszonym utworzy rekord o tej samej liczbie, są prawie całkowicie zagwarantowane, jeśli dostaniesz wystarczającą ilość danych w systemie.Potem zaczynają się wokół niego hacki i cała ta rzecz przekształca się w kupę parującego bałaganu.
Ignorowanie ogromnych systemów rozproszonych ( klastrów ) staje się kompletnym koszmarem, gdy zaczniesz próbować udostępniać dane również innym systemom. Zwłaszcza, gdy drugi system nie jest pod twoją kontrolą.
Ostatecznie masz ten sam problem, jak uczynić swój identyfikator globalnym unikalnym.
UUID został utworzony i ustandaryzowany z następującego powodu:
UUID
mogą wystąpić wszystkie wyżej wymienione problemy, w zależności od tego, któregoVersion
używasz.Version 1
używa adresu MAC i czasu, aby utworzyć unikalny identyfikator. Jest to złe, ponieważ przenosi semantyczne informacje o lokalizacji i czasie. Sam w sobie nie stanowi to problemu, kiedy naiwni programiści zaczynają polegać na tych informacjach w logice biznesowej. To powoduje także wyciek informacji, które można wykorzystać w próbach włamania.Version 2
wykorzystuje użytkownikówUID
lubGID
domianUID
lubGUI
miejsce z tego czasuVersion 1
jest tak samo złe jakVersion 1
wyciek danych i ryzykuje, że informacje te zostaną wykorzystane w logice biznesowej.Version 3
jest podobny, ale zastępuje adres MAC i czasMD5
hashem o pewnej tablicybyte[]
z czegoś, co zdecydowanie ma znaczenie semantyczne. Nie ma wycieków danych, o którebyte[]
można się martwić, nie można ich odzyskaćUUID
. Daje to dobry sposób na deterministyczne tworzenieUUID
formularzy instancji i pewnego rodzaju klucza zewnętrznego .Version 4
opiera się tylko na liczbach losowych, co jest dobrym rozwiązaniem, nie zawiera absolutnie żadnych informacji semantycznych, ale nie jest deterministycznie odtwarzalne.Version 5
jest jak,Version 4
ale używasha1
zamiastmd5
.Klucze domeny i klucze danych transakcyjnych
Moje preferencje dotyczące identyfikatorów obiektów domeny to używanie
Version 5
lub,Version 3
jeśli zVersion 5
jakiegoś powodu technicznego jest to ograniczone .Version 3
doskonale nadaje się do danych transakcyjnych, które mogą być rozłożone na wiele komputerów.Jeśli nie jesteś ograniczony przestrzenią, użyj UUID:
Gwarantujemy unikalność, zrzucanie danych z jednej bazy danych i ponowne ładowanie do innej, nigdy nie musiałeś się martwić o zduplikowane identyfikatory, które faktycznie odnoszą się do różnych danych domeny.
Version 3,4,5
są całkowicie nieprzejrzyste i właśnie takie powinny być.Możesz mieć jedną kolumnę jako klucz podstawowy za pomocą,
UUID
a następnie możesz mieć złożone unikalne indeksy dla tego, co byłoby naturalnym złożonym kluczem podstawowym.Pamięć masowa również nie musi
CHAR(36)
. Możesz przechowywaćUUID
w natywnym polu bajt / bit / liczba dla danej bazy danych, o ile jest ona nadal indeksowana.Dziedzictwo
Jeśli masz typy RAW i nie możesz ich zmienić, nadal możesz wyodrębnić je w kodzie.
Używanie
Version 3/5
zUUID
was może przejść wClass.getName()
+String.valueOf(int)
postacibyte[]
i mają nieprzezroczyste klucza odniesienia, który jest recreatable i deterministyczny.źródło
C-> A -> B -> A
iB
umieszcza się wCollection
wtedyA
i wszystkie jego dzieci są jeszcze osiągalne, te rzeczy nie są całkowicie oczywiste i może prowadzić do subtelnych przecieków .GC
jest najmniejszym z problemów, serializacja i deserializacja wykresu jest koszmarem złożoności.Tak, korzyści są w obu przypadkach, a także kompromis.
List<int>
:User
Users
tabelęList<Book>
:Jeśli nie masz problemów z pamięcią lub procesorem, z którymi bym poszedł
List<Book>
, kod korzystający zUser
instancji będzie czystszy.Kompromis:
Podczas korzystania z Linq2SQL, kod wygenerowany dla encji Użytkownik będzie miał
EntitySet<Book>
leniwy ładowany podczas uzyskiwania do niego dostępu. To powinno utrzymywać twój kod w czystości, a instancja użytkownika mała (pod względem wielkości pamięci).źródło
Krótka i prosta zasada:
Identyfikatory są używane w DTO .
Odwołania do obiektów są zwykle używane w obiektach warstwy Logika domeny / Logika biznesowa i UI.
To wspólna architektura w większych, wystarczająco przedsiębiorczych projektach. Będziesz miał twórców map, którzy tłumaczą tam iz powrotem na te dwa rodzaje obiektów.
źródło
BookRepository
aUserRepository
. Zawsze będziesz dzwonićmyRepository.GetById(...)
lub podobnie, a repozytorium albo utworzy obiekt i załaduje jego wartości ze składnicy danych, albo pobierze go z pamięci podręcznej. Ponadto obiekty potomne są przeważnie ładowane z opóźnieniem, co również zapobiega konieczności radzenia sobie z bezpośrednimi odniesieniami kołowymi w czasie budowy.