Zawsze stosowałem ograniczenia na poziomie bazy danych oprócz moich modeli (ActiveRecord). Ale zastanawiałem się, czy to jest naprawdę wymagane?
Trochę tła
Niedawno musiałem przetestować jednostkę podstawową metodę automatycznego generowania znaczników czasu dla modelu. Zwykle test utworzyłby instancję modelu i zapisałby ją bez sprawdzania poprawności. Są jednak inne wymagane pola, które nie mają wartości null w definicji tabeli, co oznacza, że nie mogę zapisać instancji, nawet jeśli pominę sprawdzanie poprawności ActiveRecord. Zastanawiam się więc, czy powinienem usunąć takie ograniczenia z samej bazy danych i pozwolić ORM je obsłużyć?
Możliwe korzyści, jeśli pominę ograniczenia w db, imo -
- Może modyfikować regułę sprawdzania poprawności w modelu bez konieczności migrowania bazy danych.
- Może pominąć walidację podczas testowania.
Możliwa wada?
Jeśli jest możliwe, że sprawdzanie poprawności ORM zakończy się niepowodzeniem lub zostanie ominięte, baza danych nie sprawdza ograniczeń.
Co myślisz?
EDYCJA W tym przypadku używam frameworka Yii , który generuje model z bazy danych, stąd też generowane są reguły bazy danych (chociaż ja też zawsze mogłem pisać je po generowaniu).
Odpowiedzi:
Waszą naczelną zasadą powinno być: Nie powtarzaj się :
ORM jest zasadniczo dodatkową warstwą (lub warstwą, jeśli wolisz), wygodnie leżącą między aplikacją a magazynami danych. Ograniczenia powinny znajdować się w jednym miejscu i tylko w jednym, niezależnie od tego, czy będzie to ORM, czy przechowywanie danych, w przeciwnym razie wkrótce skończysz utrzymywać różne ich wersje. Ty naprawdę nie chcesz tego robić.
Jednak w praktyce większość pół przyzwoitych ORM automatycznie generuje dużą liczbę modeli na podstawie schematu danych. Mimo że nadal występuje powielanie, szanse na piekło utrzymania są minimalne, ponieważ za każdym razem generowany jest zduplikowany kod ORM. Idealnie byłoby nie mieć zduplikowanego kodu, ale kolejna najlepsza rzecz to automatycznie generowane ograniczenia.
Ponadto posiadanie ograniczeń w jednym miejscu niekoniecznie oznacza, że powinieneś mieć wszystkie ograniczenia w tym samym miejscu. Niektóre, takie jak referencyjne ograniczenia integralności, mogą lepiej pasować do przechowywania danych (ale mogą zostać utracone, jeśli przejdziesz do innego magazynu danych), a niektóre, w większości te dotyczące skomplikowanej logiki biznesowej, lepiej pasują do Twojego ORM. Lepiej byłoby mieć wszystkie jabłka w tym samym koszu, ale…
Awarie
Wspominasz o niepowodzeniu ORM. Jest to absolutnie nieistotne dla twojego pytania, twoja aplikacja powinna traktować ORM i przechowywanie danych jako jeden podmiot. Jeśli zawiedzie, nie powiedzie się, ominięcie ORM w celu bezpośredniego połączenia z pamięcią danych nie jest dobrym pomysłem.
Omijanie ORM dla czegokolwiek innego
Również nie jest to dobry pomysł. Może się to jednak zdarzyć z różnych powodów:
Starsze części aplikacji, które zostały zbudowane przed wprowadzeniem ORM.
To jest trudne i dokładnie taka sytuacja mam teraz do czynienia , stąd moje ciągłe powtarzanie „utrzymania piekła”. Albo utrzymujesz części inne niż ORM, albo przepisujesz je, aby użyć ORM. Druga opcja może początkowo mieć większy sens, ale jest to decyzja oparta wyłącznie na tym, co dokładnie robią te części aplikacji i na ile wartościowe byłoby całkowite przepisanie na dłuższą metę.
Spróbuj zmienić klucz w źle zaprojektowanej tabeli MySQL 2 * 10 ^ 8 wierszy (bez przestojów), a zrozumiesz, skąd pochodzę.
Nietypowe części aplikacji, które absolutnie muszą bezpośrednio komunikować się z przechowywaniem danych:
Jeszcze trudniejsze. ORM to wymyślne narzędzia, które zajmują się prawie wszystkim, ale czasem przeszkadzają, a nawet są absolutnie bezużyteczne. Modne hasło (tak naprawdę buzzphrase) to niedopasowanie impedancji obiektowo-relacyjnej , po prostu mówiąc, ORM nie jest technicznie możliwe do zrobienia wszystkiego , co robi relacyjna baza danych, a dla niektórych rzeczy, które robią, istnieje znaczna obniżka wydajności.
Komentarze
W tym przypadku dodanie dodatkowej warstwy byłoby niezwykle pomocne, a jeśli mówimy o aplikacji internetowej, wybrałbym interfejs API REST. Zbyt uproszczonym projektem byłoby:
ORM będzie znajdować się między interfejsem API a magazynami danych, a wszystko, co kryje się za interfejsem API (łącznie z nim), będzie uważane za jedną całość z różnych aplikacji.
źródło
To jest naprawdę bardzo trudne pytanie i uważam, że jest to bardzo kontrowersyjny temat.
Jak zauważył Yannis Rizos w swojej odpowiedzi, posiadanie logiki ograniczeń zarówno w bazie danych, jak i w warstwie ORM wydaje się naruszać DRY, co „może prowadzić do koszmarów związanych z utrzymaniem, złego faktoringu i logicznych sprzeczności”.
Jednak usunięcie logiki ograniczeń z bazy danych i zachowanie jej tylko w warstwie ORM nie działałoby, jeśli spełniony jest jeden z następujących warunków:
Ręczne aktualizacje DB (wydaje się, że zdarzają się w każdej firmie)
Aktualizacje DB z innego systemu, który nie zawsze może łatwo współdzielić logikę ograniczeń ORM (np. / Skrypt Perla, który wykonuje rutynowe zadania, gdy warstwa ORM jest zaimplementowana w Hibernacji i używana przez aplikację Java do codziennej aktywności)
Sugerowałoby to, że dodajesz logikę ograniczeń do bazy danych i usuwasz z warstwy ORM , aby zapobiec naruszeniu SUCHEGO. Może to jednak prowadzić do przypadków, w których kod aplikacji nie może z powodzeniem uchwycić faktycznego problemu i przekazać go użytkownikowi (chociaż jako programista debugujący problem najprawdopodobniej możesz). W przypadku niektórych projektów może to być nie do przyjęcia.
Ostatnią opcją jest zautomatyzowanie tworzenia ograniczeń w ORM (i dowolnym innym systemie) na podstawie ograniczeń DB (lub, naprawdę ... odwrotnie). Chociaż ostatecznie skończysz z dwiema lub więcej implementacjami ograniczeń, nie będzie to naruszeniem zasady DRY opisanej w „The Pragmatic Programmer”, ponieważ zalecają one wykorzystanie generowania kodu w celu uniknięcia naruszeń DRY. Oczywiście nie jest to takie proste, ponieważ na przykład każda zmiana ograniczenia DB może wymusić przebudowę i ponowne wdrożenie wszystkich aplikacji, które z niego korzystają (nie jest to trywialne w automatyzacji).
Naprawdę należałoby to ocenić indywidualnie dla każdego przypadku . Mogę ci powiedzieć, że do tej pory spotkałem się z pustymi spojrzeniami, gdy sugeruję, aby logika ograniczeń nie była powtarzana.
źródło
Zdecydowanie dodałbym ograniczenia do bazy danych jako moją domyślną opcję. Wynika to z faktu, że w przedsiębiorstwie najważniejsze są dane, a jakość danych ma ogromne znaczenie. @Yannis Rizos wprowadził do dyskusji zasadę SUCHEGO. Kolejną zasadą jest Obrona w głębi. W przypadku danych skorzystałbym z tej zasady.
Pracowałem w prawdziwych przedsiębiorstwach, w których DB ma dane utworzone 30 lat temu. To było i nadal jest dostępne przez aplikację COBOL, a teraz przez aplikację .Net. Za 10 lat może to być aplikacja producenta, która wie. Nastąpiło połączenie, a miliony wierszy danych przekonwertowano i przeniesiono z innej firmy do tej bazy danych za pomocą SQL. Żadna ORM nie może tego zrobić. Najważniejsze jest pozostanie danych, zmiany aplikacji, zmiana sposobu generowania danych. Dlaczego więc nie zmniejszyć ryzyka uszkodzenia danych?
źródło
Myślę, że robisz to do pewnego stopnia.
Główne ograniczenia powinny istnieć w ORM - języki programowania są znacznie bardziej elastyczne, łatwiej jest je testować i łatwiej dostosowywać, gdy zmieniają się wymagania; przynajmniej nie musisz się martwić poprawkami DDL. I generalnie unikasz trudnych do przetestowania problemów z regresją danych.
Niektóre bardzo twarde i szybkie ograniczenia powinny również istnieć w bazie danych. Nie mówię na przykład o nazwach, które nie mają wartości zerowych. Mówię o takich rzeczach, jak integralność referencyjna lub wymaganie pewnych absolutnie kluczowych identyfikatorów. Wymagania strukturalne, aby Twój kod nie musiał zajmować się „co, jeśli Zamówienie zawiera nieistniejący produkt”.
źródło
Baza danych jest IMO jedynym miejscem, w którym DRY może zostać naruszone, ponieważ jeśli coś omija Twój ORM i ma złe dane, to tyle. Koniec gry. Uszkodzenie danych jest zabójczym ciosem.
źródło