Jako weteran hibernowania obiektów (w służbie od wersji 2.0) pozwolę sobie dziś na nieco prowokującego posta z lekkim dystansem do mainstreamu...:)
Pomiędzy światem relacyjnym a obiektowym istnieje oczywista niezgodność paradygmatów. Wynika z tego szereg (powiedzmy delikatnie) niezręczności - dosyć wyczerpująca lista tu: Object Relational Impedance Mismatch.
/*
* Na marginesie dodam, że c2.com jest nieprzebraną
* kopalnią wiedzy - co prawda ascetyczną,
* ale pełną materiałów uzupełnionych
* wnikliwymi przemyśleniami, polecam.
*/
Żeby wymienić te bardziej istotne "niezręczności":
- Enkapsulacja - a właściwie jej brak:) Nie ma to jak przykładowa kolekcja pozycji na zamówieniu:
order.getItems()
wybebeszona na wierzch aby można było ją radośnie modyfikować bez wiedzy obiektu order. Czy ktoś dziś pamięta jeszcze od dobrej praktyce zwracania Iteratora? W naszym przypadku mielibyśmy order.getItemsIterator()
, który pozwala jedynie bezpiecznie przejrzeć pozycje. Natomiast jakakolwiek modyfikacja musi być wykonywana przez zamówienie, np: order.addItem(Item)
. Co na to daje? Ano obiekt zamówienie lepiej od nas powinien wiedzieć co z tym fantem zrobić (gdzie dodać, a może coś przeliczyć):PDobrze, że mapery od jakiegoś czasu wspierają prywatne pola bez getterów/setterów. Można coś w ten sposób powalczyć.
- Typy danych (grafy vs krotki) - w OO mamy wskaźniki/referencje, natomiast w świecie relacyjnym iloczyny kartezjańskie wartości. Krotki jak to krotki - nie zawsze muszą mieć sens;)
Przy okazji: czy ktoś może mi podać racjonalny powód, dla którego w JPA muszę zawsze używać słowa DISTINC w zapytaniach aby uchronić się przed produktem iloczynu? Przykładowo gdy chcę pobrać listę zamówień a wraz z nimi dociągnąć pozycje to otrzymam zduplikowane obiekty zamówień - z uwagi na kartezjan z pozycjami. Pokutuje tu niestety myślenie relacyjne. Przecież wydaję zapytanie o graf obiektów a nie o jakiś zakichany iloczyn!
- Dane/odpowiedzialność - myślenie relacyjne jest nastawione na dane, obiektowe powinno być nastawione na zachowanie.
Zatem wydawać by się mogło, że sensowne mapowanie relacyjno-obiektowe wymagać musi nie lada akrobacji umysłowych. Jednak nie koniecznie:) Zamiast rozwiązać problem (np uznając, że się nie da) udajemy radośnie, że wszystko jest ok:)
Standardowe wykorzystanie ORM jest zawieszone niejako pomiędzy dwoma światami ze swymi żałosnymi namiastkami obiektów zredukowanymi do karykaturalnych paczek danych - Anemic Model.
Nie należy oczywiście popadać w paranoję i dążyć do tego, że każdy kod musi być obiektowy. Jeżeli nie ma takiej potrzeby/możliwości to pewnie, że nie. Ale po co się wówczas wydurniać z całym tym mapowaniem, które i tak jest nieudolne? Czy zamiast sztampowej klasy
Person
z polem firstName
nie wystarczy hehe result[35]
? No dobra, to przegięcie - niech będzie resultMap.get(Fileds.FIRST_NAME)
? Po co się wydurniać i oszukiwać, że mamy jakieś OO. O co walczyć w przypadkach gdy kod (encji jak i cała reszta) i tak nie jest obiektowy lecz proceduralny?
Czy nie będzie to tylko niepotrzebny narzut pasujący jak kwiatek do kożucha - że zakończę kontrowersyjnie jak obiecałem;P
//==============================
Oprócz filozoficznych zagadnień pojawiają się również całkiem praktyczne:
- modelować począwszy od bazy czy warstwy obiektów domenowych? Ja mam swoje zdanie sprawdzone w boju: Życia nie oszukasz - jeżeli tylko jest taka możliwość zacznij od obiektów (mapery tutaj akurat robią dobrą robotę generując automagicznie bazę z klas - stosować tylko w fazie "szału tworzenia")
- W kontekście Doman Drive Design: metody biznesowe w encjach oczywiście nie są fajne, gdy chcemy przesyłać encje z/do klientów. Pytanie czy na pewno chcemy je tam przesyłać? Naiwne mapowanie (zamapowane wszystkie powiązania z bazy) pozwala dodając zamówienie dodać razem z nim gdzieś tam w niezachermetyzowanych bebechach encję nowego usera z fajnymi uprawnieniami:)
W małych systemach na pewno wygodnie jest posługiwać się encjami we wszystkich warstwach, ale w większych jednak oldskulowe DTO pozwoli ukryć strukturę modelu biznesowego.