728 x 90

Zrób to dobrze – dobre praktyki tworzenia aplikacji webowych w ASP.NET MVC cz.1

Zrób to dobrze – dobre praktyki tworzenia aplikacji webowych w ASP.NET MVC cz.1

Wstęp

Artykuł jest pierwszym z serii, opisującej dobre praktyki tworzenia aplikacji webowych, na przykładzie ASP.NET MVC.

W tym artykule opisany zostanie etap wyboru architektury.

Natywna architektura MVC

Wzorca architektonicznego MVC nie trzeba przedstawiać nikomu, kto chociaż w podstawowym stopniu zagłębił się w tworzenie aplikacji webowych. Dzięki podzieleniu aplikacji na trzy główne warstwy, osoby specjalizujące się w różnych gałęziach programowania, mogą pracować nad różnymi komponentami aplikacji, bez większego ryzyka wystąpienia konfliktów w kodzie, np. projektanci interfejsu mogą zająć się widokami, specjaliści baz danych rozwojem bazy i mapowaniem jej na model, a programiści “serwerowi” implementowaniem logiki aplikacji.

Kolejną zaletą stosowania wzorca MVC jest również, w założeniu, możliwość ponownego użycia kodu. Jako, iż model jest niezależny od widoku, może być użyty do wytworzenia innej aplikacji, np. dedykowanej, mobilnej wersji danej aplikacji. Widoki również mogą zostać ponownie użyte w innej aplikacji, poprzez zmianę odwołań do poszczególnych pól modelu.

Minusem wzorca MVC jest potrzeba posiadania przez programistę przynajmniej podstawowych umiejętności z każdej dziedziny. Aby skutecznie dokonywać zmian w całym projekcie należy znać technologie serwerowe, związane z interfejsami, jak również bazami danych, co nie miało jeszcze aż takiego znaczenia w przypadku ASP.NET Web Forms i innych technologiach z ery przed MVC.

Warto też zaznaczyć, że ponowne użycie kodu w solucji niezwiązanej z pisanym obecnie programem (tworzenie tzw. szablonów programów), może być problematyczne, ponieważ pisząc w natywnej architekturze MVC należałoby przekopiować osobno każdy plik lub wyłuskać pojedyncze metody z klas programu – cały projekt zawierać bowiem będzie również logikę specyficzną dla danego rozwiązania, która z dużym prawdopodobieństwem nie będzie spójna z innym programem, pisanym w przyszłości.

Architektura Cebulowa

Pomimo wielu zalet, wzorzec MVC nie rozwiązuje jednego z dużych problemów inżynierii oprogramowania– ścisłej zależności poszczególnych warstw projektu między sobą. Kontroler wykonuje logikę biznesową, wywołując przy tym operacje na bazie danych, za pośrednictwem modelu i zwracając je do widoku. Komponenty te są od siebie w rezultacie ściśle zależne (ang. tight
coupling). W przypadku potrzeby wprowadzenia zmian, na przykład aktualizacji technologii odpowiadającej za warstwę danych, zmienić trzeba będzie również warstwę kontrolera. Proces aktualizacji technologii stanowczo się wydłuży, co generuje większy koszt dla strony biznesowej, a w rezultacie ucierpi na tym konkurencyjność rozwiązania informatycznego.
Odpowiedzią na ten problem jest Architektura Cebulowa (ang. Onion Architecture). Głównym założeniem jest wprowadzenie luźnych powiązań (ang. loose coupling) pomiędzy poszczególnymi obiektami / warstwami aplikacji tak, aby ograniczyć w jak największym stopniu zależność jednej warstwy od drugiej, dzięki czemu wprowadzenie zmian w projekcie będzie łatwiejsze i mniej czasochłonne. W tym celu wydziela się szereg warstw:

  • Domain Model – rdzeń aplikacji (ang. Application Core). Przechowuje wszystkie obiekty domenowe. Nie powinno stosować się tutaj składni dla jakiejkolwiek technologii zewnętrznej, tj. takiej, która nie wchodzi, w przypadku tej pracy, w skład podstawowej biblioteki .NET. Klasy tu zdefiniowane nie powinny zawierać logiki biznesowej, jedynie odwzorowanie struktury danych,
  • Domain Services – odpowiada za definicje sposobu kontaktu ze źródłem danych, czyli zawiera interfejsy, za pomocą, których aplikacja będzie pozyskiwać dane,
  • Application Services – odpowiada za definicje sposobu kontaktu z klasami logiki biznesowej, czyli zawiera interfejsy, za pomocą, których warstwa interfejsu użytkownika (ang. User interface), będzie pozyskiwać dane zgodne z wymaganiami klienta (odpowiednio przetworzone, przeliczone, przefiltrowane, walidowane itp.),
  • User interface – odpowiada za prezentację danych pozyskanych z warstwy Application Services oraz umożliwia użytkownikowi przesłanie danych do serwera aplikacji,
  • Infrastructure – w tej warstwie znajdują się implementacje interfejsów zdefiniowanych w Application Services i Domain Services (osobne projekty obu rodzajów interfejsów),
  • Tests – warstwa zawierająca testy aplikacji.
    Onion Architecture

    Architektura Cebulowa

Definicja interfejsów dla repozytoriów oraz klas logiki biznesowej pozwala na użycie zabiegu Wstrzykiwania Zależności (ang. Dependency Injection). Zamiast definiowania w kodzie klasy, która konkretna implementacja interfejsu ma zostać użyta, zależności te rozwiązywane są podczas działania programu. Dzięki temu, projekty są między sobą luźno powiązane, tzn. tylko warstwa Domain Services wie, jak łączyć się ze źródłem danych i na nich operować oraz tylko Application Services posiada wiedzę na temat logiki biznesowej. Dzięki zastosowaniu interfejsów, zmiany w projekcie są stosunkowo proste – wystarczy napisać nową klasę dziedziczącą po danym interfejsie i określić w kodzie, iż od teraz to ona implementuje dany interfejs. W założeniu, dokonanie takiej modyfikacji powinno sprowadzać się do edycji tylko jednej linii programu, a nie, jak w przypadku domyślnej implementacji MVC, zmiany wielu plików klas, korzystających ze zmodyfikowanej klasy.

Zależności w obu architekturach

Domyślna architektura MVC praktycznie wyklucza możliwość ponownego użycia napisanego kodu (z powodu tight coupling) w innej solucji oraz znacząco utrudnia wprowadzanie zmian, ponieważ wszystkie warstwy aplikacji napisane są w jednym projekcie.

Architektura MVC

Zależności w solucji, w domyślnej architekturze MVC.

W celu uzyskania lepszych możliwości adaptacyjnych, zastosowano Architekturę Cebulową:

Architektura Cebulowa

Zależności w solucji, w domyślnej architekturze cebulowej.

Projekt Helper jest dodatkiem do standardowo opisanej Architektury Cebulowej i zawiera metody oraz zmienne niepowiązane bezpośrednio z logiką biznesową, których użycie możliwe jest w wielu modułach (projekt z globalnymi klasami).

Jak widać, kierunek zależności jest zawsze jednostronny i skierowany do warstw tzw. niższych, co rozwiązuje problem ścisłej zależności wszystkich komponentów od siebie, widoczny w MVC. Umożliwia to o wiele łatwiejszą modyfikację logiki aplikacji. Wydzielenie osobnego projektu na interfejs (projekt Web) pozwala też np. zlecić prace w tym projekcie dedykowanemu Front-End developerowi, a prace nad logiką aplikacji dedykowanemu Back-End developerowi, co zwiększa jakość pisanego kodu.

Podsumowanie

W ramach tego artykułu przedstawiony został problem szeroko stosowanego wzorca architektonicznego MVC, związany ze ścisłą zależnością komponentów oraz zaprezentowane zostało rozwiązanie, w postaci rozbicia jednego projektu, realizującego wszystkie funkcjonalności, na szereg mniejszych projektów, odpowiedzialnych za pojedyncze, logiczne komponenty aplikacji, co realizuje wzorzec architektoniczny Cebula.

Źródła:

1] Obraz tytułowy: https://www.pexels.com/photo/schedule-planning-startup-launching-7376/

2] Grafiki: https://medium.com/@monica85rodrigues/clean-architecture-onion-architecture-ddd-cqs-outras-cenas-264d064b3c16

Leave a Comment

Your email address will not be published. Required fields are marked with *

Cancel reply

Inne artykuły