Abstrakcja między logiką biznesową a ORM

0

Witam,

Uczę się teraz ASP.NET MVC. Doszedłem do punktu w którym mam wygenerowany model na podstawie bazy danych(podejście Database First) i doszedłem do implementowania "logiki biznesowej". Kontroler "odwołuje się do logiki biznesowej poprzez Dependency Injection(NInject)- wstrzykuję konkretny fragment modelu biznesowego(np dodaj użytkownika, dodaj produkt, edytuj produkt itd) do konstruktora odpowiedniego kontrolera. Mam pytanie jak rozwiązać odwoływanie się modelu biznesowego do ORM (Entity Framework). Tworząc poszczególne klasy bardzo odwołuję się do konkretnego DbContext wygenerowanego przez Entity. A co jeśli sama baza a w konsekwencji wygenerowany przez EF model ulegnie znaczącej zmianie ? Wtedy konieczna stanie się modyfikacja logiki biznesowej tak aby nazwy do których się odwołuję były zgodne z tymi na nowo wygenerowanymi przez EF. W jaki sposób tego uniknąć ? Czy w praktyce tworzy się jakąś wastwę abstrakcji między ORM a logiką biznesową ? Jęśli tak to jak to w skórcie wgląda, ew w którym kieruku szukać ?

Pozdrawiam,
W2K

1

Pomiędzy "czystą" bazę danych a resztą aplikacji możesz skorzystać ze wzorca repozytorium i Unit of Work:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Wtedy masz coś co pozwala Ci uniezależnić resztę kodu od konkretnej bazy.

0

Czysta tzn ? Taka już opakowana w ORM ?

0

@W2K chodzi chyba oto, ze trzyma logike biznesowa w klasach wygenerowanych przez ORM (lub w klasach zaleznych od wygenerowanych klas przez ORM).
Coz, i tak zawsze musisz cos zmienic.
Mozesz trzymac logike w partial classes ale wtedy dalej jestes uzalezniony od zmian w bazie.
Mozesz wprowadzic dodatkowa warstwe - klasy biznesowe (ktorych obiekty sa tworzone na podstawie obiektow zwroconych przez ORM) i mapper ktory bedzie mapowal obiekty zwrocone przez ORM na obiekty biznesowe. - wtedy przy zmianach modyfikujesz mapper i czasem rowniez klasy biznesowe.
Tak czy siak zawsze cos musisz modyfikowac.

0

Chodziło mi o szczegóły drugiej opcji . Tak żeby warstwa biznesowa nie była bezpośrednio zależna od obiektyw bazy

0
W2K napisał(a):

A co jeśli sama baza a w konsekwencji wygenerowany przez EF model ulegnie znaczącej zmianie ? Wtedy konieczna stanie się modyfikacja logiki biznesowej tak aby nazwy do których się odwołuję były zgodne z tymi na nowo wygenerowanymi przez EF.

To nie jest problem, przecież można zmieniać nazwy wygenerowane przez EF.

W2K napisał(a):

Chodziło mi o szczegóły drugiej opcji . Tak żeby warstwa biznesowa nie była bezpośrednio zależna od obiektyw bazy

I jeszcze database first do tego?

To musisz mieć repozytoria - w sensie interfejsy na których operuje (pobiera i zapisuje dane) logika biznesowa.
Musisz mieć implementację tych repozytoriów, które będą korzystały z DbContextu.
Musisz mieć też interfejsy dla swoich wygenerowanych z bazy danych klas (czyli encji) (i repozytoria będą operowały na tych interfejsach). Bez nich i tak musisz mieć zależność między wygenerowanym DbContextem, a Twoją logiką biznesową.
(Jeśli to jest niejasne, to daj znać, narysuję jakiś przykład.)

No cóż, podejście database first jest po prostu słabe jeśli mamy zamiar wprowadzać zmiany w bazie... Czyli właściwie zawsze. ;)

0

Może o jakiś prosty diagram bym poprosił ;-)
A co do Database Firrst, to myśałem że w sumie to się najczesciej stosuje w praktyce ?
Jak w praktyce rozwiązuje się problem z połączniem logiki z bazą danych jeśli mamy już gotową baze ?

6

@W2K, @Varran, sorry że tak długo, ale nie mogłem znaleźć weny do rysowania. ;)

W2K napisał(a):

Może o jakiś prosty diagram bym poprosił ;-)

Zakładając, że celem jest izolacja logiki biznesowej od warstwy dostępu do danych w przypadku code first wygląda to jak na poniższym diagramie:
97d1081cc1.png

Mamy naszą domenę, w której znajdują się: encje, serwisy i repozytoria. Repozytoria są oczywiście interfejsami, a ich konkretne implementacje znajdują się w warstwie składowania. Tam też znajduje się konfiguracja mapowań (XML, klasy mapujące, konwencje - zależy co chcemy i na co pozwala nam nasz ORM). Wszystko tu jest czytelne i oczywiste, w jednym module mamy klasy odpowiedzialne za logikę biznesową, w drugim za dostęp do bazy, logika biznesowa nic o bazie nie wie.

W przypadku Entity Framework i Database First sprawa się komplikuje, bo kod z generatora jest pomieszaniem encji - czyli logiki biznesowej z kontekstem i informacjami o mapowaniach, czyli warstwą bazodanową. Żeby osiągnąć efekt, o który nam chodzi, musimy się napracować więcej, co obrazuje poniższy diagram:
60a853a17e.png

Model EF jest nienaruszalny, więc musimy zmieniać domenę. Encje w niej nie są już klasami lecz interfejsami, które są implementowane przez encje z EF. Właściwości są w klasach wygenerowanych automatycznie, natomiast dodatkowe metody dopisujemy do klas partial. Repozytoria też musiały się zmienić - operują na interfejsach encji, a nie ich implementacjach. Warstwa składowania zmieniła się o tyle, że korzysta z modułu z modelem EF, w którym znajdują się mapowania i DbContext, z którego korzystają implementacje repozytoriów. Model z EF nie może znajdować się w tym samym module, co repozytoria, gdyż spowodowałoby to cykliczną zależność między Domain.dll i Persistence.dll, dlatego niestety musi być w oddzielnym module.

A co do Database Firrst, to myśałem że w sumie to się najczesciej stosuje w praktyce ?

Trudno mi powiedzieć, czy najczęściej. Ale zapewne wiele osób, które uważają, że tworzenie aplikacji trzeba zaczynać od bazy, tak właśnie robi. Tylko, że to nie jest wydajna metoda tworzenia oprogramowania. Zaczynając od kodu, można łatwo wprowadzać zmiany w domenie, np. dodanie nowej właściwości albo zmiana powiązania między klasami jest szybka. Można stosować podejście TDD, pisać testy przed implementacją i osiągnąć wysoki poziom pokrycia testami. Możemy nawet zbudować prototyp aplikacji, pokazać go klientowi, a dopiero gdy stwierdzi, że mu wszystko pasuje, wygenerować bazę danych.
Jeśli zaczynamy od bazy, to każdą zmianę w modelu danych najpierw musimy wprowadzić w bazie, a potem odświeżamy model, co zazwyczaj wymaga późniejszych ręcznych poprawek. Głupia zmiana nazwy/typu właściwości zajmuje kilka minut zamiast sekund.

Jak w praktyce rozwiązuje się problem z połączniem logiki z bazą danych jeśli mamy już gotową baze ?

Jak kto woli. Moim zdaniem to, co narysowałem na diagramie, to przesada. Trzeba dopisać sporo rzeczy, które właściwie dublują już istniejący kod. Zmiany też trzeba będzie wprowadzać w kilku miejscach.
Osobiście przy database first wolę zostawić wygenerowany przez EF model (nawet nie dopisywać do niego żadnych metod), opakować go w serwisy biznesowe, a bardziej skupić się na odizolowaniu warstwy GUI od encji. Mam na myśli to, że obiekty wyświetlane i edytowane w GUI nie są encjami lecz jakimiś ViewModelami, które dopiero w warstwie biznesowej mapowane są na encje. Zmiany w strukturze danych, poza koniecznością odświeżenia modelu z bazy, powodują wyłącznie zmiany w serwisach biznesowych. Nie jest to co prawda szczyt inżynierii oprogramowania, ani nawet programowania obiektowego, daleko temu do DDD, ale jak pisałem - próba zrobienia z database first czegoś sensownego to gra raczej nie warta świeczki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1