Dzisiaj wdałem się w gorącą debatę z innym programistą w mojej organizacji na temat tego, gdzie i jak dodawać metody do klas odwzorowanych w bazie danych. Używamy sqlalchemy
i główna część istniejącej bazy kodu w naszych modelach baz danych to niewiele więcej niż worek mapowanych właściwości o nazwie klasy, prawie mechaniczne tłumaczenie z tabel bazy danych na obiekty python.
W moim argumencie moja pozycja była taka, że podstawową wartością użycia ORM było to, że możesz dołączyć zachowania i algorytmy niskiego poziomu do mapowanych klas. Modele są najpierw klasami, a następnie trwałymi (mogą być trwałe przy użyciu xml w systemie plików, nie musisz się tym przejmować). Jego zdaniem każde zachowanie jest „logiką biznesową” i koniecznie należy do dowolnego miejsca, ale nie w modelu trwałym, który ma być używany tylko do trwałości bazy danych.
I na pewno sądzę, że istnieje różnica między tym, co jest logika biznesowa i powinny być rozdzielone, ponieważ ma jakąś izolację od niższego poziomu, jak to zostanie zrealizowane, a logika domeny, które wierzę, jest abstrakcją dostarczone przez klasach modelowych spierałem się w poprzednim akapicie, ale ciężko mi wskazać, co to jest. Mam lepsze pojęcie o tym, co może być interfejsem API (którym w naszym przypadku jest HTTP „ReSTful”), ponieważ użytkownicy wywołują interfejs API z tym, co chcą robić , niezależnie od tego, co wolno im robić i jak to zrobić robi się.
tl; dr: Jakie rzeczy mogą lub powinny iść w metodzie klasy odwzorowanej przy użyciu ORM i co należy pominąć, aby żyć w innej warstwie abstrakcji?
źródło
Odpowiedzi:
Jestem głównie z tobą; wydaje się, że twój kolega opowiada się albo za anemicznym modelem domeny antywirusowej, albo za duplikowaniem modelu w „modelu trwałości” bez wyraźnych korzyści (pracuję nad projektem Java, w którym to zostało zrobione, i jest to ogromny problem z utrzymaniem, ponieważ oznacza to trzykrotność pracy przy każdej zmianie modelu).
Ogólna zasada: klasa powinna zawierać logikę opisującą podstawowe fakty dotyczące danych, które są prawdziwe we wszystkich okolicznościach. Logika specyficzna dla przypadku użycia powinna być gdzie indziej. Przykładem jest walidacja, ciekawy artykuł Martina Fowlera, w którym podkreśla, że należy go uznać za zależny od kontekstu.
źródło
Jest to wezwanie do osądu, które naprawdę zależy od przewidywanego rozmiaru i skali tego, co rozwijasz. Najbardziej sztywnym podejściem jest ograniczenie typów ORM do komponentu dostępu do danych i stosowanie POCO we wspólnej bibliotece jako typów, do których odwołują się i których używają wszystkie warstwy. Umożliwiłoby to w przyszłości fizyczną separację, a także logiczną separację. Możesz także zdecydować, że między interfejsem użytkownika a warstwą logiki biznesowej powinna istnieć dodatkowa warstwa. Zwykle jest to nazywane warstwą fasadową lub biznesową. W tej dodatkowej warstwie znajduje się „kod przypadku użycia”. Indywidualny luźno powiązany kod jest wywoływany przez warstwę Facade / BI (np. Facade ma funkcję ProcessOrder (), która wywołuje logikę biznesową 1: M razy, aby wykonać wszystkie kroki konieczne do faktycznego przetworzenia zamówienia).
Jednak wszystko to powiedziane: wielokrotnie ta ilość architektury jest po prostu niepotrzebnym przesadą. Na przykład, kod specjalnie dla prostej witryny sieci Web, w której nie masz zamiaru pakować jej komponentów do ponownego użycia. Tworzenie witryny internetowej MVC i używanie obiektów EF dla tego typu rozwiązania jest całkowicie poprawne. Jeśli witryna będzie musiała zostać później skalowana, możesz przyjrzeć się tworzeniu klastrów lub procesowi często tracącemu nazwę „refaktoryzacji”.
źródło
Przypomnij tylko swojemu współpracownikowi, że nie musisz nadarchitagować modeli, jakby to był projekt Java. Chodzi mi o to, że porównywanie dwóch utrwalonych obiektów jest zachowaniem, ale nie jest ono określone przez warstwę trwałości. Pytanie 6 piwa brzmi: dlaczego zupełnie niepowiązane zajęcia opisują coś o tym samym? Oczywiście, wytrwałość jest wystarczająco dużym aspektem modelu, aby traktować go osobno, ale niewystarczająco uzasadniającym, aby traktować go inaczej niż wszystko inne. Jeśli prowadzisz samochód, myjesz go lub bijesz, cały czas manipulujesz samochodem.
Dlaczego więc nie połączyć wszystkich tych różnych aspektów w jedną klasę modeli? Potrzebujesz kilku klasowych metod radzenia sobie z utrwalonymi obiektami - umieść je w jednej klasie; masz wiele metod instancji zajmujących się sprawdzaniem poprawności - umieść je w innej. Wreszcie, zmieszaj dwa i voila! Masz tutaj inteligentną, samoświadomą iw pełni ograniczoną reprezentację modelu.
źródło
Oprócz innych odpowiedzi, zwracaj uwagę na ukryte zastrzeżenia, używając bogatych modeli domen z ORM.
Miałem problemy z wstrzykiwaniem usług polimorficznych w klasach modeli utrwalonych, gdy próbowałem osiągnąć coś takiego jak następujący pseudokod:
W takim przypadku organizacja może wymagać
HRService
zależności konstruktora (na przykład). Zwykle nie można łatwo kontrolować instanacji klas modeli podczas korzystania z ORM.Korzystałem z Doctrine ORM i kontenera usług firmy Symfony. Musiałem śledzić ORM w niezbyt elegancki sposób i nie miałem wyboru, musiałem rozdzielić modele uporczywości i biznesowe. Nie próbowałem jeszcze z sqlachemy, pomyślałem. Python może okazać się bardziej elastyczny niż PHP dla tych rzeczy.
źródło