Archiwum kategorii: .NET Core

SMessaging – prosta architektura oparta o wiadomości

Nie ukrywam, że od dawna podoba mi się podejście do budowy aplikacji w oparciu o wiadomości. Zdecydowałem się zebrać wszystkie mi znane materiały i napisać prosty framework (przynajmniej narazie prosty), którego zadaniem będzie umożliwienie budowy aplikacji w oparciu o wiadomości i handlery.

Dzięki temu w łatwy sposób możemy w naszej aplikacji zaimplementować CQRS ale nie tylko. W internecie można znaleźć bardzo wiele implementacji tego patternu ale ja chciałem maksymalnie uprościć architekturę, przecież nie zawsze nasza aplikacja musi być tak skomplikowana aby implementować odrazu CQRS.

Kod zarówno frameworka jak i przykład wykorzystania w aplikacji znajdziecie na GitHubie. Przedstawię w skrócie przykładową implementację w oparciu o prosty system rezerwacji.

Zaczynamy od kontrolera WebAPI, gdzie wstrzykujemy interfejs IMessaging:

To jest już pierwsza równica w budowaniu aplikacji w oparciu o wiadomości. Tradycyjnie tu mielibyśmy jakiś serwis aplikacyjny lub odrazu repozytorium dostępu do DB i w metodach robili jakieś operacje CRUD. Natomiast w tym podejściu zawsze do kontrolera będzie wstrzykiwany jeden interfejs, którego zadaniem jest przekazanie wiadomości przychodzącej „gdzieś” dalej, a przecież każdy request od użytkownika, każdy json, który leci do naszego API to tak naprawdę jakaś wiadomość, np. CreateOrder, GetOrder czy ShowListOfCars.

W takim podejściu nie ważne czy implementacja jest w serwisie, czy może w zupełnie innym miejscu. Bardzo upraszcza to kontroler WebAPI, jego odpowiedzialność to teraz tylko odebrać wiadomość, przekazać ją dalej i obsłużyć wynik lub ewentualny wyjątek:

Czy nie jest prościej?

Obsługę wiadomości implementujemy w handlerze, w dowolnym miejscu naszej aplikacji:

Zadaniem handlera jest wykonać daną operację i zwrócić wynik w postaci bardzo prostej struktury MessageResult. Dla operacji typu command czyli takich, które zmieniają stan aplikacji będzie to wyglądało podobnie ale wynik nie będzie potrzebny:

W taki sposób nasza aplikacja właściwie „sama” implementuje część patternów CQRS, stosując podejście handler per wiadomość właściwie w naturalny sposób odeseparowywujemy wiadomości pobierające dane od zmieniających stan aplikacji.

Aby włączyć framework należy go dodać w klasie konfigurującej zależności, w przypadku ASP.NET Core w klasie Startup:

To chyba tyle jeśli chodzi o prosty przykład. W przyszłości planuję rozwinąć framework o obsługę prostej kolejki i możliwość przesyłania wiadomości po sieci tak aby możliwe było zbudowanie rozproszonego systemu. Zapraszam do pobierania kodu z GitHuba.

Framework jest również dostępny do pobrania w NuGet.

 

Testy DB przy użyciu EntityFramework Core

Tak zgadza się, ten post będzie o testowiu warstwy dostępu do bazy danych. Pewnie zadajesz sobie pytanie po co testować bazę danych. Zgadzam się z tym w pełni w wielu przypadkach nie ma takiej potrzeby… Przecież można stworzyć interfejs, który w teście zamockuję i po sprawie…Tak to prawda w wielu przypadkach takie podejście będzie wystarczające ale co jeśli np. otrzymaliśmy w spadku system legacy i nie ma czasu na refactor całego rozwiązania, a chcemy napisać test integracyjny kontrolera MVC / WebAPI czy serwisu WCF.

Jeśli do tej pory używaliśmy EntityFramework czy jakiegoś innego Frameworka w naszej aplikacji to pewnym problemem podczas próby napisania testu był stan naszej aplikacji inaczej mówiąc dane jakie mamy w bazie danych. Pojawiało się pytanie czy podczas uruchamiania testów wykonywać je na specjalnym środowisku, utrzymywać specjalną bazę danych tylko pod testy, generować dane pod testy i na końcu sprzątać po sobie, może test uruchamiać w traksakcji… Wszystkie te czynności zajmowały dużo czasu….

I tu z pomocą przychodzi EntityFrameworkCore i InMemory provider dzięki, któremu możemy nasze testy uruchamiać niezależnie od bazy danych, a zarazem testować operacje CRUD na danych.

Jeśli używasz w swoich aplikacjach pełnego „starego” .NET, i nie masz możliwości użyć .NET Core nie kończ proszę czytać w tym miejscu. Ponieważ EF Core możesz uruchomić w normalnej aplikacji .NET i zaraz pokażę jak…

Na początek tworzę normalny projekt ASP.NET WebApi, jedyne wymaganie do wybranie docelowego frameworka minimum w wersji 4.6.1.

Dodajmy teraz paczkę Nuget dla EF Core, w tym celu instaluję paczkę Microsoft.EntityFrameworkCore.SqlServer ponieważ w projekcie WebAPI chcę użyć SQL Server.

Dzięki biblioteką .NET Standard możliwe jest pisanie aplikacji, które możemy uruchomić w dowolnym środowisku .NET, nie inaczej jest z EF Core!

Właściwie jeśli chodzi o podejście nic się nie zmieniło pomiędzy EF 6 a EF Core (EF Core to tak naprawdę EF 7), definiujemy klasę dla naszego kontekstu i używamy DbSet dla obsługi poszczególnych encji. Jedyna różnica to sposób w jaki konfiguruje się parametry połączenia, wcześniej przekazywało się connectionString poprzez kontruktor. Teraz przekazuje się DbContextOptions i korzysta się z DbContextOptionsBuilder do konfiguracji tego np. we frameworku Dependency Injection.

Użycie kontekstu w aplikacji praktycznie niczym się różni:

Przejdźmy teraz do testów. Aby umożliwić użycie EF Core i providera InMemory należy zainstalować paczkę  Microsoft.EntityFrameworkCore.InMemory.

Dzięki temu w naszym teście możemy skonfigurować DbContext tak aby wszystkie operacje były wykonywane w pamięci. Prosty test sprawdzający czy dane prawidłowo zostały zapisane w bazie danych wygląda następująco:

Celowo dodałem bloki using aby zobrazować potencjał jaki drzemie w tego typu podejściu. Moglibyśmy spreparować odpowiedni DbContext i uruchamiać na nim całą serię testów naszych CRUD-ów. Zawsze gdy tworzony jest nowy kontekst bazy danych EF korzysta wewnętrznie z cache i dane, które już zapisaliśmy są dostępne zupełnie jak byśmy korzystali z bazy danych!

Podsumowując myślę, że naprawdę warto się zastanowić nad wykorzystaniem tego Frameworka, koszt przejścia z wersji EF 6 do EF Core nawet jeśli korzystamy ze „starego” .NET nie powinien być duży. Dzięki temu możemy zaoszczędzić sporo czasu i już nie zastanawiać się jak zapewnić prawidłowe dane pod testy.