Czy ORM umożliwiają tworzenie bogatych modeli domen?

21

Po korzystaniu z Hibernacji w większości moich projektów przez około 8 lat, znalazłem się w firmie, która zniechęca do jego używania i chce, aby aplikacje współdziałały z bazą danych tylko za pomocą procedur przechowywanych.

Po zrobieniu tego przez kilka tygodni nie byłem w stanie stworzyć bogatego modelu domenowego aplikacji, którą zaczynam budować, a aplikacja wygląda jak (okropny) skrypt transakcyjny.

Niektóre z problemów, które znalazłem, to:

  • Nie można nawigować po wykresie obiektowym, ponieważ procedury przechowywane po prostu ładują minimalną ilość danych, co oznacza, że ​​czasami mamy podobne obiekty z różnymi polami. Jeden przykład to: mamy procedurę składowaną do pobierania wszystkich danych od klienta, a drugą do pobierania informacji o koncie plus kilka pól od klienta.
  • Wiele logiki kończy się na klasach pomocniczych, więc kod staje się bardziej uporządkowany (z jednostkami używanymi jako stare struktury C).
  • Bardziej nudny kod rusztowania, ponieważ nie ma frameworku, który wyodrębniałby zestawy wyników z procedury składowanej i umieszczałby ją w encji.

Moje pytania to:

  • czy ktoś był w podobnej sytuacji i nie zgodził się z podejściem do procedury sklepu? co zrobiłeś?
  • Czy istnieje faktyczna korzyść ze stosowania procedur przechowywanych? oprócz głupiego punktu „nikt nie może wydać tabeli upuszczania”.
  • Czy istnieje sposób na utworzenie bogatej domeny przy użyciu procedur przechowywanych? Wiem, że istnieje możliwość użycia AOP do wstrzykiwania DAO / repozytoriów do bytów, aby móc poruszać się po grafie obiektowym. Nie podoba mi się ta opcja, ponieważ jest bardzo bliska voodoo.

Wniosek

Po pierwsze, dziękuję wszystkim za odpowiedzi. Doszedłem do wniosku, że ORM nie pozwalają na tworzenie modeli Rich Domain (jak niektórzy wspominali), ale upraszcza to (często powtarzalne) prace. Poniżej znajduje się bardziej szczegółowe wyjaśnienie wniosku, ale nie jest ono oparte na żadnych twardych danych.

Większość aplikacji żąda i wysyła informacje do innych systemów. Aby to zrobić, tworzymy abstrakcję w terminach modelu (np. Zdarzenie biznesowe), a model domeny wysyła lub odbiera zdarzenie. Wydarzenie zwykle wymaga niewielkiego podzbioru informacji z modelu, ale nie całego modelu. Na przykład w sklepie internetowym bramka płatności żąda pewnych informacji o użytkowniku i kwoty całkowitej, aby obciążyć użytkownika, ale nie wymaga historii zakupów, dostępnych produktów i całej bazy klientów. Wydarzenie ma więc mały i konkretny zestaw danych.

Jeśli weźmiemy bazę danych aplikacji jako system zewnętrzny, musimy stworzyć abstrakcję, która pozwoli nam zmapować jednostki Modelu Domeny do bazy danych ( jak wspomniał NimChimpsky , używając mapera danych). Oczywista różnica polega na tym, że teraz musimy ręcznie opracować mapowanie dla każdej encji modelu do bazy danych (starszego schematu lub procedur przechowywanych), z tym dodatkowym utrudnieniem, że skoro nie są one zsynchronizowane, jedna encja domeny może częściowo mapować do encji bazy danych (np. klasa UserCredentials, która zawiera tylko nazwę użytkownika i hasło, jest mapowana na tabelę użytkowników, która ma inne kolumny), lub jedna encja modelu domeny może być mapowana na więcej niż jedną encję bazy danych (na przykład, jeśli istnieje relacja jeden do jednego jedno mapowanie na stole, ale chcemy wszystkich danych w jednej klasie).

W aplikacji z kilkoma jednostkami dodatkowa praca może być niewielka, jeśli nie ma potrzeby poprzecznego przekształcania jednostek, ale zwiększa się, gdy istnieje warunkowa potrzeba poprzecznego przenoszenia jednostek (i dlatego możemy chcieć zaimplementować jakiś rodzaj „leniwego” Ładuję'). W miarę, jak aplikacja ma coraz więcej encji, ta praca po prostu się zwiększa (i mam wrażenie, że rośnie nieliniowo). Zakładam tutaj, że nie próbujemy wynaleźć ORM.

Jedną z korzyści traktowania DB jako systemu zewnętrznego jest to, że możemy kodować sytuacje, w których chcemy uruchomić 2 różne wersje aplikacji, w których każda aplikacja ma inne mapowanie. Staje się to bardziej interesujące w scenariuszu ciągłych dostaw do produkcji ... ale myślę, że jest to również możliwe w przypadku ORM w mniejszym stopniu.

Odrzucę aspekt bezpieczeństwa, ponieważ programista, nawet jeśli nie ma dostępu do bazy danych, może uzyskać większość, jeśli nie wszystkie informacje przechowywane w systemie, po prostu wstrzykując złośliwy kod (np. Nie mogę uwierzyć, że zapomniałem usunąć wiersz rejestrujący dane karty kredytowej klientów, drogi panie! ).


Mała aktualizacja (6/6/2012)

Procedury przechowywane (przynajmniej w Oracle) zapobiegają robieniu czegoś takiego jak ciągłe dostarczanie z zerowym przestojem, ponieważ każda zmiana struktury tabel spowoduje unieważnienie procedur i wyzwalaczy. Dlatego podczas aktualizacji bazy danych aplikacja również nie działa. Oracle zapewnia rozwiązanie dla tego zwanego Redefinition opartym na edycji , ale kilka DBA, o które pytałem o tę funkcję, wspomniało, że była ona słabo zaimplementowana i nie umieściłaby jej w produkcyjnym DB.

Augusto
źródło
No, oczywiście ty mógł robić to, co robi i wykorzystanie Hibernate Dziedziczenie generowania dynamicznego obiektu proxy, które pozwalają odzyskać wykres obiektu. Jest to jednak bardzo hackingowe w przypadku SP: D
Max.
Skończyłobym na ponownym zainicjowaniu połowy hibernacji, bez ponad 10-letniego doświadczenia zespołu hibernacji :).
Augusto
1
Wszelkie DBA powinny zapobiegać upuszczaniu określonych tabel przez niektórych użytkowników. Nie powinno mieć znaczenia, jak to zrobisz.
JeffO
1
Możesz rzucić okiem na Mybatis - może dostarczyć potrzebną funkcję. To mniej ORM niż środowisko mapowania. Możesz pisać SQL, jak chcesz i powiedzieć Mybatis, gdzie umieścić go w swoim modelu obiektowym. Będzie obsługiwał wykresy dużych obiektów z wieloma zapytaniami, co brzmi jak twoja sytuacja (wiele cienkich procedur przechowywanych).
Michael K
1
@Augusto: Byłem w podobnej sytuacji, nie z powodu użycia SP, ale z powodu zastrzeżonych ram odwzorowania, które nie wspierały relacji między obiektami. Spędziliśmy dni, pisząc kod, który można napisać w kilka minut przy użyciu odpowiedniej ORM. Nigdy nie udało mi się rozwiązać tego problemu.
kevin cline

Odpowiedzi:

16

Twoja aplikacja powinna być nadal modelowana na podstawie zasad projektowania opartych na domenie. Niezależnie od tego, czy używasz ORM, zwykłego JDBC, wywoływanie SP (lub cokolwiek innego) nie powinno mieć znaczenia . Mamy nadzieję, że cienka warstwa oddzielająca twój model od SP powinna załatwić sprawę w tym przypadku. Jak stwierdzono w innym plakacie , powinieneś zobaczyć SP i ich wyniki jako usługę i zmapować wyniki do modelu domeny.

Martijn Verburg
źródło
Martijn, zgadzam się, że aplikacja powinna być modelowana przy użyciu zasad DDD, ale problem, z którym się borykam (i proszę powiedz mi, czy jest jakieś rozwiązanie !!) polega na tym, że niektóre przechowywane procesy zwracają bardzo mało informacji, aby błyskawicznie utworzyć jednostkę DDD. Zapoznaj się z tym komentarzem, w którym wyjaśniłem trochę więcej na temat informacji zwracanych przez procedury składowane. Mógłbym to obejść, wywołując więcej niż jeden zapisany proc, i na przykład pobierając wszystkie dane użytkownika, a następnie wywołując inny, aby pobrać wszystkie informacje o koncie, ale wydaje się to błędne :).
Augusto
1
@Augusto Cóż ... jesteś programistą aplikacji, więc musisz zdecydować, czy ma sens istnienie określonego obiektu z określonymi polami ustawionymi na NULL. Jeśli ma to sens (na przykład w przypadku niektórych zadań), niech tak będzie. Jeśli nie, poproś autora SP o podanie większej ilości danych, abyś mógł stworzyć swoje obiekty.
Jacek Prucia
I dodając do komentarza Jacka - w rzeczywistości całkowicie akceptowalne jest wywoływanie 2+ przechowywanych proc, ponownie pomyśl o nich jako o dwóch zdalnych usługach, które musisz wywołać, aby stworzyć swój model domeny, nic złego z tym :-).
Martijn Verburg
@Martijn: Z mojego doświadczenia wynika, że ​​cienka warstwa nie jest wystarczająca. Kod mapowania może być znacznie dłuższy niż podstawowa logika biznesowa.
kevin cline
@Kevin cline - Dobra uwaga, umieściłem „mam nadzieję” w odpowiedzi :-)
Martijn Verburg
5

Czy istnieje faktyczna korzyść ze stosowania procedur przechowywanych?

W świecie finansów (i miejscach, w których wymagana jest zgodność z Sarbanes-Oxley ), musisz mieć możliwość kontrolowania systemów, aby upewnić się, że robią to, co powinny. W takich przypadkach o wiele łatwiej jest zapewnić zgodność, gdy cały dostęp do danych odbywa się za pomocą procedur przechowywanych. A po usunięciu całego ad-hoc SQL znacznie trudniej jest ukryć różne rzeczy. Na przykład, dlaczego byłoby to „dobre”, odsyłam do klasycznego artykułu Kena Thompsona Refleksje na temat Trusting Trust .

Tangurena
źródło
tak milion razy tak! Musisz także upewnić się, że użytkownicy nie mogą robić niczego, czego nie powinni robić, w tym nie mają bezpośrednich praw do tabel i przechowywanych procedur, które bardzo to pomagają.
HLGEM,
1
Pracuję dla spółki publicznej i jesteśmy zgodni z SOX. Może to być moja słaba wiedza na temat kontroli, ale nie widzę różnicy między przeprowadzaniem kontroli na poziomie bazy danych (za pośrednictwem przechowywanych procesów) lub na poziomie aplikacji. Każda aplikacja powinna mieć swój własny schemat DB, który jest dostępny tylko z aplikacji, a nie współużytkowany przez różne aplikacje.
Augusto
uszkodzony link ...
Alex R
@AlexR, stały link
Tangurena
2

Procedury przechowywane są znacznie bardziej wydajne niż kod SQL po stronie klienta. Wstępnie kompilują SQL w bazie danych, co pozwala mu również przeprowadzać optymalizacje.

Architektonicznie SP zwróci minimalną ilość danych wymaganych dla zadania, co jest dobre, ponieważ oznacza, że ​​przesyłanych jest mniej danych. Jeśli masz taką architekturę, musisz pomyśleć o DB jako o usłudze (pomyśl o tym jako o usłudze sieciowej, a każdy SP jest metodą wywoływania). Praca z nim w ten sposób nie powinna stanowić problemu, podczas gdy ORM poprowadzi cię do pracy ze zdalnymi danymi tak, jakby były lokalne, w ten sposób nakłaniając do wprowadzenia problemów z wydajnością, jeśli nie będziesz ostrożny.

Byłem w sytuacjach, w których całkowicie korzystaliśmy z SP, DB dostarczyło API danych i korzystaliśmy z niego. Ta konkretna aplikacja była bardzo duża i działała niesamowicie dobrze. Po tym nie będę miał nic złego do powiedzenia na temat SP!

Jest jeszcze jedna zaleta - DBA napiszą dla ciebie wszystkie zapytania SQL i z przyjemnością obsłużą całą hierarchię relacyjną w DB, więc nie musisz.

gbjbaanb
źródło
3
gbjbaanb, większość tego, co powiedziałeś, jest prawdą dla starszych baz danych. Większość nowszych baz danych dość często rekompiluje zapytania, aby zdecydować, które nowe optymalizacje zastosować (nawet jeśli są przechowywane). Zgadzam się z tym, co powiedziałeś o używaniu bazy danych jako systemu zewnętrznego, ale widzę to również jako dużo pracy, ponieważ aplikacja jest właścicielem bazy danych i obie powinny być zsynchronizowane w jak największym stopniu. Na przykład z nazwami tabel / klas i pól / kolumn. Ponadto podejście polegające na tym, że DBA zapisują procedury, pachnie raczej silosami programistycznymi niż wielodyscyplinarnym zespołem.
Augusto
7
SP nie zawsze są bardziej wydajne i myślę, że przekazywanie SQL DBA to zły sposób. Jako ekspert w dziedzinie programista powinien wiedzieć, jakie dane chce uzyskać i jak je zdobyć
Martijn Verburg
1
To dobra odpowiedź, ale z mojego doświadczenia wynika, że ​​większość klientów tak naprawdę nie potrzebuje przyrostu wydajności do kontrolowania dostępu do danych za pomocą procedur przechowywanych w porównaniu do niedogodności związanych z pełnym wykorzystaniem narzędzi ORM w warstwie aplikacji. Najczęściej widzę te decyzje architektoniczne podejmowane w sklepach z oprogramowaniem, w których muszą one uzasadnić rozdęte pensje programistów „szarej brody” , którzy nie mają innych umiejętności.
wałek klonowy
1
@Augusto the approach of letting the DBAs write the procedures smells like development silos+100 internetów do ciebie za ten klejnot prawdy. Zawsze widziałem to jako przypadek, w którym dostęp do danych był kontrolowany za pomocą procedur przechowywanych.
wałek klonowy
1
@maple_shaft: dlaczego DBA piszący SP nie są częścią zespołu programistów? Tam, gdzie to działa, są specjalistami od kodowania, którzy znają ten aspekt systemu naprawdę dobrze, znacznie lepiej niż większość programistów ogólnego przeznaczenia. Może to być problem, który doprowadził do popularności ORM. Chodzi mi o to, że nikt nie pomyślałby dwa razy o tym, aby projektant wykonał GUI, więc dlaczego nienawidzę architekta danych wykonującego schemat?
gbjbaanb
2

Często zdarza się, że programiści nieprawidłowo używają swoich obiektów ORM jako modeli domen.

Jest to niepoprawne i wiąże domenę bezpośrednio ze schematem bazy danych.

To, co naprawdę powinno mieć, to oddzielne modele domen tak bogate, jak chcesz i osobno używaj warstwy ORM.

Oznacza to, że będziesz potrzebować mapowania między każdym zestawem obiektów.

ozz
źródło
1
To dobry pomysł, ale w przypadku mniejszych projektów naprawdę zaczyna się wydawać, że to przesada. To podejście wymaga również warstwy translacji między warstwą trwałości ORM a modelem domeny.
wałek klonowy
@maple_shaft zgodził się i to miałem na myśli przez „mapowanie” :-)
ozz
@Ozz, sposób w jaki pracowałem jest dokładnie taki, że klasy jednostek SĄ modelem domeny (i mogę dodać z całkiem sporym sukcesem). Zgadzam się, że wiąże model domeny ze schematem, ale właśnie tego chcę, ponieważ używam konwencji nad konfiguracją, a fajnym efektem ubocznym jest to, że jeśli widzę pole na encji, nie muszę się mocno zastanawiać o nazwie tabeli i kolumny, w której przechowywane są te informacje.
Augusto
@Augusto Też to zrobiłem! i jak mówi maple_shaft, jest to w porządku dla małych aplikacji w stylu CRUD, ale jest wiele problemów, gdy OP się dowiaduje. Jednym z przykładów może być tabela mapowania wiele do wielu, np .: StudentClasses, która mapuje Studentów do ich klas i zawiera tylko StudentID i classID, niekoniecznie chciałbyś zmapować to w swojej domenie. To tylko szybki przykład mojej głowy.
ozz
2
@Ozz: Twój komentarz wydaje się być sprzeczny z samą ideą ORM. ORM nie „wiąże domeny bezpośrednio ze schematem bazy danych”. ORM odwzorowuje twoją domenę na schemat DB, bez potrzeby oddzielnej warstwy DAO. To jest sedno ORM. Większość ORM dobrze sobie radzi z mapowaniem wiele do wielu, bez potrzeby tworzenia modelu domeny dla tabeli mapowania.
kevin cline
1

Obiekty domeny można zapełnić w dowolny sposób, nie jest konieczne używanie Hibernacji. Myślę, że właściwym terminem jest mapowanie danych . Bardzo możliwe, że utrwalone dane będą miały zupełnie inną strukturę niż obiekty domeny.

Nim Chimpsky
źródło
Obecnie używamy maperów danych, ale problem polega na tym, że przechowywane procesy zwracają minimalny zestaw danych, który czasami nie wystarcza do wypełnienia obiektu (być może powinniśmy pozwolić procedurom przechowywanym na zwrócenie większej ilości informacji). Na przykład jeden proc proc może zwrócić e-mail użytkownika, imię, nazwisko; podczas gdy inny dodaje identyfikator użytkownika i adres. Ponieważ dane są różne, do przechowywania danych używamy różnych obiektów, co oznacza, że ​​mamy różne klasy „Użytkownik”. Staram się tutaj unikać dziedziczenia, ponieważ uważam, że to niewłaściwe użycie.
Augusto
@Augusto: interfejsy?
Kramii przywraca Monikę
@Karmii, nie sądzę, aby interfejsy tutaj pomogły, ponieważ wówczas musielibyśmy powielić logikę w różnych klasach. Lub możemy użyć interfejsów, a następnie delegować przetwarzanie do klasy pomocniczej, ale to nie jest tak naprawdę OO :(.
Augusto
1
@Augusto Nie rozumiem problemu: „przechowywane procs zwracają minimalny zestaw danych, który czasem nie wystarcza do wypełnienia obiektu” Więc modyfikujesz sproc lub tworzysz inny, a następnie pozwalasz mapowaniu danych wykonać mapowanie
NimChimpsky