Z pamiętnika SaaSa: Zalety korzystania z NHibernate w aplikacji SaaS

Z pamiętnika SaaSa

W tym artykule przedstawię plusy i minusy wykorzystania ORM NHibernate (NH) w aplikacji SaaS. Będzie to spojrzenie z perspektywy SoloProgramisty, czyli jednej osoby, która tworzy komercyjną aplikację SaaS.

Co to jest ORM?

Zanim przejdę do opisania zalet NHibernate, wspomnę czym wogóle jest to narzędzie. ORM (Object-Relational Mapping) jest to sposób na korzystanie w swojej aplikacji z bazy danych. Zamiast bezpośrednich zapytań SQL, programista korzysta z klas i metod języka programowania, w którym pisze aplikację.

NHibernate jest to ORM na platformie .NET. Inną popularną biblioteką jest Entity Framework, tworzony przez firmę Microsoft. Nie będę porównywał obu narzędzi, gdyż w swoich projektach od wielu lat korzystam wyłącznie z NHibernate. Jest duża szansa, że część wniosków, które odnoszą się do NHibernate, będą również prawdziwe do Entity Frameworka. Pamietaj jednak, iż te narzędzia nie mają dokładnie takiej samej funkcjonalności, dlatego niektórych rzeczy nie znajdziesz w produkcie Microsoftu.

Korzystanie z ORM ma swoje plusy i minusy:

Plusy:

  • Do operacji na bazie danych korzystasz z klas modelowych, których używasz w pozostałej części logiki biznesowej. Zmiany dokonane w tych klasach automatycznie mają odzwierciedlenie w zapytaniach do bazy
  • Wiele czynności jest wykonana automatycznie, np. mapowanie rezultatów zwracanych przez zapytanie do klas modelowych
  • Programista nie musi znać języka SQL
  • Kod związany z bazą danych jest niezależny od konkretnego silnika bazodanowego, więc można zmienić bazę danych bez zmiany kod programu
  • Kod dostępu do bazy piszesz w znanym Ci języku programowania

Minusy:

  • Musisz nauczyć się nowego narzędzia
  • Gorsza wydajność w porównaniu z zapytaniami SQL (stworzonymi przez eksperta od SQL)
  • ORM wprowadza abstrakcję, która ukrywa wiele szczegółów przed programistą. Osoba bez doświadczenia może mieć sporo problemów i nawet o tym nie wiedzieć (np. pobieranie wieszy z bazy wysyłając N zapytań – jedno na każdy wiersz)

NHibernate dla SoloProgramisty

Powyższa lista zalet i wad ORM tyczy się oczywiście także NHibernate. Czy w takim razie warto go stosować w swojej aplikacji SaaS? Czy gorsza wydajność nie przekreśla od razu tego narzędzia? W końcu możliwość zmiany silnika bazodanowego na inny w teorii brzmi fajnie, ale w praktyce nikt tego nie robi, prawda?

Nie prawda 😉

Najpierw powiem Ci, dlaczego minusy ORM nie są dużym problemem, a następnie opiszę jakie zalety NH wykorzystuję w swoich aplikacjach i jak bardzo ułatwiają mi życie.

Minusy

Musisz nauczyć się nowego narzędzia

Jest to oczywiście prawda. Będziesz musiał poświęć sporo czasu, aby dobrze poznać NH. A jest co poznawać. Ma on dziesiątki różnych ficzerów, 3 sposoby na pisanie zapytań (QueryOver, HQL i Linq), dwa rodzaje sesji (standardowa i stateless) itp. Taka jest cena skorzystania z zalet tej biblioteki.

W karierze programisty będziesz poznawać wiele języków, dziesiątki bibliotek i frameworków. Nie jest dobrym pytaniem, czy warto uczyć się nowych narzędzi, a raczej które narzędzie wybrać. Uważam, że nie ma sensu poznawać wielu ORM – skup się na jednym i poznaj go na poziomie zaawansowanym. Jeśli znasz już EntityFramework i zastanawiasz się, czy warto wchodzić w NH, to powiem Ci, że nie wiem. Na pewno NH ma parę unikatowych ficzerów, ale czy one uzasadniają poświęcony czas na naukę?

Uważam, że lepiej znać bardzo dobrze NH, niż średnio NH i EF. Im lepiej poznasz ORM, tym więcej z niego wyciśniesz i tym mniejsze znaczenie będą miały następne minusy, które opisuję poniżej.

Ja NH wykorzystałem już we wszystkich moich aplikacjach SaaS (możesz poczytać o nich tutaj), a dodatkowo w kilku innych projektach, w których brałem udział w trakcie swojej kariery zawodowej. Z tej perspektywy, czas poświęcony na poznanie NH, dawno się zwrócił. Moim zdaniem programista powinien skupić się na nauce tych języków i frameworków, których będzie używał w większości swoich programów. Jeśli nie widzisz szansy na korzystanie z jakiejś biblioteki, to nie trać za wiele czasu, aby się jej nauczyć. Zapoznaj się z możliwościami i wystarczy. Naucz się rzeczy, które faktycznie Ci się przydadzą – dlatego uważam, że warto znać jedno (i tylko jedno) narzędzie ORM.

Gorsza wydajność w porównianiu z zapytaniami SQL

Zwykle jest to prawda, ale nie zawsze. Jeśli porównamy zapytania SQL stworzone przez eksperta od baz danych, to NHibernate nie ma szans. Dialekty SQL dla poszczególnych baz danych oferują bardzo wiele zaawansowanych funkcji, których właściwe użycie, może przyśpieszyć nasz program.

Jednak kluczem tutaj jest słowo właściwe użycie, a także ekspert od baz danych. Bo jeśli (jak wielu programistów) ekspertem nie jesteś i znasz SQL na poziomie max średnio zaawansowanym, to prawdopodobnie i tak nie wykorzystujesz wielu ficzerów dostępnych w bazach danych. A jeśli, co gorsza, znasz SQL w stopniu podstawowym, to może się okazać, że zapytania, które byś stworzył, będą mniej wydajne niż te stworzone przez NH!

Zakładam, że jako soloprogramista, musisz ogarniać wiele różnych technologii i języków programowania, żeby stworzyć własny program, więc ekspertem od SQL nie jesteś. W związku z tym, nie rozpaczałbym za bardzo nad teoretyczną utratą wydajności zapytań.

Drugą kwestią jest to, czy wysoka wydajność jest wogóle wymagana w Twojej aplikacji. Często jest tak, że nie. Jeśli nie masz dzięsiątek jednoczesnych użytkowników, to najprawdopodobniej problem wydajności u Ciebie nie istnieje. Prawidlowo wykorzystany NH ma wydajność wystarczającą dla większości średniej wielkości aplikacji. Na początkowym etapie projektu, dla SoloProgramisty, wysoka wydajność nie jest potrzebna. Ważniejsze są ficzery i plusy, które NH oferuje.

ORM wprowadza abstrakcję, która ukrywa wiele szczegółów przed programistą

Z jednej strony jest to plus (np. nie trzeba znać języka SQL). Jednak ta cecha może powodować wiele problemów, o których programista nawet nie będzie wiedzieć. Relacyjne bazy danych są to bardzo złożone narzędzia, które działają w określony sposób. Operują one na tabelach i wierszach, a język SQL bardzo mocno różni się od języka obiektowego (C#). Programista, który nie zna jezyka SQL i nie wie jak działają bazy danych, może bardzo łatwo napisać kod, który przy większej ilości danych będzie bardzo niewydajny.

Co więcej, nieświadomy programista, nie będzie nawet wiedzieć dlaczego kod, który napisał, pobiera dane z bazy tak długo – w końcu w kodzie C# jest to tylko jedna linijka. Dopiero skorzystanie z Sql Profilera uwidoczniłoby, iż ta jedna linijka w C# pobiera 100 wierszy z bazy pojedyńczo, wiersz po wierszu.

Problemów, może być oczywiście więcej. Dlatego uważam, że chcąc skorzystać z zalet NH, trzeba go dobrze poznać i do tego Cię zachęcam.

Minusy dla SoloProgramisty

Podsumowując wady NH i ORM, uważam, że dla SoloProgramisty, minusy są pomijalne. Na pewno, aby wykorzystać możliwości tego typu narzędzi, należy je dobrze poznać. Jednak, jeśli poświęcisz czas na zapoznanie się z nimi, to o wiele łatwiej i szybciej, będziesz mógł tworzyć swoje projekty.

A teraz najważniejsza część, czyli co NHibernate może Ci zaoferować.

Plusy

Dostajemy wiele ficzerów przyśpieszających tworzenie projektu

Mam na myśli takie ficzery jak: automatyczne mapowanie danych z bazy na klasy modelowe, lazy loading, cache, wyeliminowanie podatności SQL Injection, możliwość wykorzystania klas używanych do pobierania danych z bazy, w pozostałej części logiki biznesowej itp. Większość z tych rzeczy spokojnie mógłbyś zaimplementować własnoręcznie korzystając z ADO.NET i zapytań SQL, jednak wymagałoby to o wiele więcej pracy i czasu. A jak pisałem w artykule o soloprogrammingu, to czasu akurat nie mamy za wiele.

Kod jest niezależny od bazy danych, co umożliwia jej zmianę

Często słyszałem opinie, że zmiana bazy danych w projekcie, który używa NHibernate jest możliwa, ale prawie nikt tego nie robi. Bo po co zmieniać Sql Server na MySql, gdy program już działa i są już użytkownicy? A gdy dopiero tworzymy aplikację, to jak często dochodzimy do wniosku, że jednak MySql lepiej się sprawdzi, skoro jeszcze nie mamy realnych danych oraz metryk?

Zgadzam się w 100% – dla mnie zmiana bazy dla naszej aplikacji jest czysto teoretyczną możliwością. Sam tego nigdy nie zrobiłem. Ale to nie znaczy, że ten ficzer NHibernate jest zbędny. Wręcz przeciwnie. Dla mnie jest to chyba najwięszka zaleta NH i właśnie dla niej korzystam z tego ORM w każdym swoim projekcie.

Zmiana bazy ma sens gdy weźmiemy pod uwagę bazę plikową SqLite. Ta baza jest niesamowita, gdy chcemy w prosty sposób dodać do naszej aplikacji następujące elementy:

  • Testy integracyjne logiki biznesowej
  • Testy End To End (E2E)
  • Tryb Live Demo naszej aplikacji

Poniżej znajdziesz krótki opis powyższych ficzerów. W najbliższym czasie planuję napisać serię artykułów wyjaśniających jak za pomocą NH i C# je zaimplementować.

Testy integracyjne logiki biznesowej

W standardowym podejściu do testów automatycznych, zdecydowana większość testów w naszym projekcie to powinny być testy jednostkowe. Pozostałe rodzaje testów sugeruje się żeby były dodatkiem. Jednak w przypadku aplikacji, która jest (nie)typowym CRUDem, większość testów automatycznych to testy integracyjne logiki biznesowej. Moim zdaniem w tego typu aplikacjach nie ma zbyt wiele komponentów, które nadają się do testowania jednostkowego. Moduły, które testujemy w takich projektach to np. Tworzenie faktury. Logika biznesowa takiego komponentu mogłaby wyglądać mniej więcej tak:

  1. Sprawdź, czy faktura z takim numerem jest już w bazie
  2. Jeśli nie ma to pobierz dane o kliencie
  3. Dodaj do bazy nową fakturę
  4. Zwiększ w bazie licznik faktur o 1

Powyższy algorytm jest oczywiście wymyślony na potrzeby tego posta, jednak jak możesz zauważyć, przy większości kroków mamy kontakt z bazą danych. Niektórzy sugerują, żeby robiąc dla takiego komponentu test jednostkowy, wydzielić cały kod związany z dostępem do bazy danych do oddzielnej klasy, a nasz komponent korzystałby z interface IInvoiceReposidory, który na potrzeby testu byłby mockiem.

Dla mnie tego typu podejście nie ma za wiele sensu. Nie wiem w jakim procencie ten test testowałby nasz komponent, a w jakim naszego mocka. W swoich aplikacjach tego typu komponenty (czyli całą logikę biznesową) testuję za pomocą testów integracyjnych, w których główne skrzypce gra baza SqLite In-Memory (w pamięci). Dzięki temu, że NHibernate umożliwia przełączanie się między różnymi bazami, mam możliwość napisania testów w taki sposób, że każdy test tworzy za pomocą SqLite w pamięci bazę danych, wypełnia ją początkowymi danymi, odpala testowany komponent, sprawdza rezultat i bez problemu czyści bazę. Dzięki temu, że baza jest w pamięci, tego typu testy są w miarę szybkie i nie ma problemu z przywracaniem bazy danych do stanu początkowego dla każdego testu. Poza tym, do uruchomienia testów nie muszę mieć na komputerze zainstlowanego Sql Servera. A co najważniejsze: test sprawdza w 100% naszą logikę biznesową – łącznie z zapytaniami do bazy!

Jak dla mnie jest to idealne podejście do testowania aplikacji SaaS. Mój najnowszy produkt easyRenti, dokładnie w taki sposób testuje praktycznie 100% logiki biznesowej.

Testy End To End (E2E)

W tradycyjnym podejściu testy E2E testują całą aplikację w „realnym” środowisku, czyli mówiąc ogólnie, taki test odpala przeglądarkę, wprowadza adres naszej aplikacji, klika po niej, wpisuje różne wartości i sprawdza, czy strona zachowuje się zgodnie z oczekiwaniami.

W mojej skrzynce narzędzowej SoloProgramisty, testy E2E zagościły dopiero od niedawna (pierwszy raz wprowadziłem podstawowe testy E2E do easyRenti). Jednak w odróżnieniu od tradycyjnego podejścja, moje jest inne – bardziej dostosowane do SoloProgramisty. Stworzenie infrastruktury do uruchamiania testów E2E jest dość pracochłonne, a co gorsza utrzymanie jej również wymaga pracy. Na takiej maszynie musimy oczywiście zainstalować Sql Server, postawić bazę danych, zadbać o to, żeby każdy test przywracał ją do stanu początkowego, a w przypadku zmian w aplikacji, które wpływają na zmianę struktury bazy, odpowiednie zmiany musimy wprowadzić za każdym razem w naszym środowisku testowym.

SoloProgramista nie ma tyle czasu. Dlatego ja podchodzę do testów E2E w inny sposób. Moje testy uruchamiają oczywiście przeglądarkę i wpisują adres (jak w tradycyjnym podejściu), jednak aplikacja nie korzysta ze standardowej bazy danych (Sql Server). Tutaj do akcji ponownie wchodzi NHibernate i jego możliwość zmiany bazy danych na SqLite (In-Memory). Tak, dobrze czytasz – moje testy E2E korzystają z bazy w pamięci!

W tym miejscu nie będę kontynuował tego tematu. Napiszę artykuł, w którym wszystko dokładnie wyjaśnię. Teraz jedynie co mogę napisać, to to, że testy E2E uruchamiane na bazie SqLite mają tak wiele plusów, że szczerze polecałbym tego typu podejście w każdym projekcie (nie tylko SoloProgramisty). A wszystko to możliwe dzięki NHibernate.

Live Demo

Jest to sposób na zaprezentowanie programu na realnych danych w jak najprostszy dla użytkowników sposób. Standardowo, osoba która chciałaby przetestować Twój program i ocenić, czy to jest to czego potrzebuje, musiałaby:

  1. Założyć konto
  2. Zalogować się do programu.
  3. W tym momencie ma przed sobą pusty program – bez żadnych danych. Żeby zobaczyć pełne możliwości aplikacji, łącznie z raportami, wykresami itp, użytkownik musiałby najpierw wypełnć nowo utworzone konto danymi. W easyRenti musiałaby wprowadzić przykładowe domy, najemców, umowy, płatności, rachunki itp.

Jest duża szansa, że po minucie „klikania” po programie, osoba by się zniechęciła i zakończyła poznawanie aplikacji.

Tryb Live Demo polega na tym, że jest stworzony specjalny użytkownik w naszym programie, na który osoba może się zalogować i od razu ma przed sobą system z wypełnionymi danymi. Od razu widzi powiadomienia, raporty, wykresy itp. Może oczywiście coś dodać, coś usunąć, a zmiany które wprowadza, nie są widoczne dla innych osób, które zalogują się na testowe konto.

Zaimplementowane takiego testowego konta, które dostarcza system z danymi za każdym razem, gdy ktoś się zaloguje, nie jest takie oczywiste. Jak izolować bazę danych dla takiego konta? Pamiętaj, że kilka osób jednocześnie może aktualnie korzystać z konta Demo i wprowadzać zmiany.

Tutaj również NHibernate załatwia sprawę. Dla konta Demo, przełączamy bazę danych z Sql Server na SqLite (tym razem baza w pliku). Wtedy za każdym razem, gdy nowa osoba loguje się na konto Demo, tworzymy na dysku nową bazę SqLite i wypełniamy ją danymi. Gdy użytkownik skończy testowanie, taki plik z bazą po prostu usuwamy.

Praktyczny przykład implementacji zaprezentuję w najbliższym czasie w oddzielnym artykule. Na razie możesz zapoznać się z działaniem trybu Live Demo na easyRenti.

Podsumowanie

Wykorzystanie NHibernate w aplikacji SaaS daje bardzo dużo możliwości, które inaczej byłoby ciężko wprowadzić do naszego projektu. Warto poznać dobrze to narzędzie, gdyż wtedy większość wad znika, a ilość dodatkowych ficzerów, które dostajemy w pakiecie jest naprawdę spora.

Jeśli chciałbyś dowiedzieć się jak użyć NHibernate w aplikacji ASP.NET Core, to zapraszam Cię do tego artykułu.

Jeśli masz doświadczenie z Entity Frameworkiem, napisz w komentarzu, czy powyższe ficzery można uzyskać korzystając z ORM Microsoftu.

Share