Warstwa biznesowa – skrypt transakcji

Najprostszym wzorcem projektowym warstwy biznesowej,  należącym do grupy wzorców proceduralnych jest skrypt transakcji (w skrócie TS – transcaction script). Spójrzmy na diagram UML przedstawiający przykład jego użycia:

image

Innymi słowy, TS jest zapisem przypadków użycia w naszym systemie. W przypadku systemu sprzedaży oczywistymi przypadki użycia są m.in.: dodanie nowego klienta do bazy, złożenie zamówienia czy pobranie listy produktów. Jak już wspomniałem jest to wzorzec proceduralny a nie obiektowy zatem nie ma żadnej zależności między obiektem biznesowym a klasą TS . Klasa implementująca TS zatem może zawierać zarówno metody związane z produktami jak i z zamówieniami.

W programie możemy zdefiniować dowolną ilość skryptów transakcji – nie ma z góry narzuconej metody podziału.

Jak widzimy wzorzec jest bardzo prosty i składa się wyłącznie z pojedynczych klas. TS to prostu zwykła klasa zawierająca metody wykonujące jakieś operacje oraz często operujące na bazie danych.

Warto również pokazać jak wykorzystać TS w połączeniu z wzorcem polecenie (ang. Command). Szczególnie pożyteczne jest to w małych aplikacjach w których implementacja interfejsu stanowi znaczący procent pracy włożonej w całość systemu. Zacznijmy od diagramu klas:

image

 

Kolejne klasy poleceń zawierają logikę zawartą w TS (albo ją po prostu wywołują). W takim przypadku łatwo zaimplementować mechanizm undo \ redo. W większych systemach ciężko użyć powyższej implementacji ale dla prostych aplikacji jest to rozwiązanie bardzo wygodne w użyciu.

Ze względu na proceduralny charakter TS, często dobrym rozwiązaniem jest zaimplementowanie TS jako singleton:

image

Oczywiście nie będziemy mogli użyć implementacji singleton w przypadku gdy nasz TS nie jest napisany w sposób bezstanowy. Pamiętajmy jednak, że TS wykorzystywany jest w małych systemach w których nie wprowadza się dodatkowej warstwy usług więc powinniśmy implementować zawsze TS bezstanowo (będzie on prawdopodobnie wykorzystywany bezpośrednio jako np. usługa sieciowa).

Na zakończenie krótkie podsumowanie w formie zalet i wad.

Zalety:

  1. prosty w implementacji,
  2. w przypadku gdy zaimplementujemy go jako polecenia bardzo łatwo wykorzystać wzorzec bezpośrednio w interfejsie,
  3. stanowi naturalne przejście z przypadków użycia (zdefiniowanych przez architekta \ analityka) na kod.

Wady:

  1. brak reguł mówiących ile tak naprawdę powinno być klas TS. W przyszłości może okazać sie ze lepiej byłoby rozdzielić napisany już TS na dwie osobne klasy,
  2. brak jawnego oddzielenia TS od warstwy dostępu do danych. Bardzo łatwo zapomnieć się i umieścić kod należący tak naprawdę do DAL w TS,
  3. wzorzec formalnie nie narzuca niezależności od źródła danych – programista musi zadbać o interfejs, który abstrahuje od typu przetwarzanego źródła danych.

Wprowadzenie do warstwy biznesowej

Zacznijmy od zdefiniowania do czego potrzebna nam jest tzw. warstwa biznesowa w systemie. Sama nazwa może nie wiele mówi i czasami okazuje się  nawet myląca.

Ogólnikowo  jest to rdzeń systemu. Stanowi zdecydowanie najważniejszy punkt każdej aplikacji. Warstwa biznesowa ( w skrócie BL – business layer) zawiera właściwą logikę aplikacji. Jeśli brzmi to zbyt abstrakcyjnie, przedstawmy to na przykładzie systemu sprzedaży (na którym będę często bazował). Co stanowi warstwę biznesową ( a więc logikę)  w systemie sprzedaży? Oczywiście składanie i walidowanie zamówień, śledzenie zmian przysyłki czy wybieranie produktów, które powinny być przecenione. Poniżej przedstawiam kilka według mnie kluczowych zadań BL:

  1. Implementacja reguł biznesowych. Regułą biznesową może być np. “przeceń produkt jeśli jego sprzedaż w ostatnim kwartale zmalała n-razy”. W najprostszym przypadku w kodzie realizujemy to za pomocą instrukcji sterujących (np. if-else). W zaawansowanych przypadkach może okazać się, że potrzebny jest tzw. silnik reguł biznesowych czyli mechanizm umożliwiający dynamicznie definiowanie reguł – w przypadku modułu promocji jest to bardzo prawdopodobny scenariusz.
  2. Walidacja danych. Sprawdzanie poprawności powinno odbywać się w każdej warstwie. W BL jednak jest to niezwykle ważne ponieważ błędne dane mogą spowodować nawet awarie systemu. Proste walidacje będą dotyczyły po prostu typu lub zakresu danych (np. czy podana wartość zawiera się w jakiś przedziale liczb). Z kolei skomplikowane reguły dotyczą wielu obiektów biznesowych jednocześnie oraz nie mają charakteru typowo technicznego( jak typ danych) a raczej biznesowy (wyznaczony przez ekspertów z danej dziedziny).
  3. Monitoring oraz wykonywanie logów. O ile nie zdecydujemy się na dodatkową warstwę usług to wszelkie monitorowanie danych powinniśmy zawrzeć właśnie w BL. Temat jest dość szeroki i samo monitorowe też nie jest takie proste – istnieje dużo technologii wspomagających ten proces. Postaram się w przyszłości o tym napisać.
  4. Buforowanie danych – sytuacja podobna, jeśli nie używamy warstwy usług to wszelkie buforowanie mające na celu przyśpieszyć obsługiwanie zgłoszeń należy zawrzeć w BL. Oczywiście buforowanie danych to również zadanie dla warstwy dostępu do danych a nawet warstwy prezentacji.
  5. Obsługa błędów – w prostym przypadku użyjemy standardowego bloku try-catch. Jednak istotniejsze jest co zrobimy z tymi danymi. W niektórych systemach wszelkie krytyczne błędy powinny być wysyłane bezpośrednio do administratorów( np. za pomocą e-mail lub wiadomości SMS). Wszystko oczywiście zależy od postawionych wymagań oraz QoS ( quality of service).

 

Wiemy, już co warstwa powinna wykonywać. Kolejna pytanie brzmi, jak to ma wykonywać? Przede wszystkim kod BL powinien być maksymalnie elastyczny i abstrahować od źródła danych. Początkujący projektanci często popełniają następujące błędy:

  1. Wywołują metody z warstwy prezentacji. Pamiętajmy, że żadna metoda w BL nie może w jakimkolwiek parametrze zawierać referencji do kodu związanego z warstwą prezentacji (w skrócie PL – presentation layer). To PL jest odpowiedzialne za przesłanie niezbędnych danych do BL a BL nie może odczytywać tych wartości samodzielnie z interfejsu użytkownika. Nawet dla najmniejszych aplikacji pamiętajcie aby umieścić BL w osobnym dll i pousuwać wszelkie referencję do warstwy prezentacji(choćby do biblioteki System.Windows.Controls).
  2. Warstwa biznesowa nie formatuje danych. Jeśli BL zwraca cenę produktu to nie w formie uwzględniającej typ oraz zapis waluty – to jest już zadanie warstwy prezentacji. Podobnie należy sobie zdać sprawę, że BL nie jest odpowiedzialna za mechanizm globalizacji – wszelkie informacje muszą być przesyłane w sposób neutralny. Interpretacją oraz wizualizacją ich zajmuje się warstwa prezentacji.
  3. Odczyt wartości z bazy danych bezpośrednio w BL. Wszelkie obliczenia w BL powinny abstrahować od typu i położenia źródła danych. Zatem jeśli potrzebujemy przetworzyć jakieś zamówienie powinniśmy operować na zwykłych klasach a nie pobierać dane bezpośrednio z bazy danych. W przypadku prostych wzorców projektowych takich jak skrypt transakcji należy skorzystać z jakiś bramek – ale to temat na następny post.

Dobre odizolowanie BL daje nam możliwość wyeksponowania kodu za pomocą np. WCF czy zwykłego web service. Należy podkreślić, że jest to właśnie ta warstwa, która odpowiada za realizację rzeczywistego problemu postawionego aplikacji. Pozostałe warstwy zależą głównie od wykorzystywanej technologii. Z kolei dobrze zaprojektowana BL powinna być maksymalnie niezależna. Dobry zwyczajem jest aby każda klasa w BL była typu POCO – ale to również temat na osobny post.

Dlaczego warto zainteresować się trójwarstwowym modelem aplikacji?

Postanowiłem, że zanim przejdę do omawiania kolejnych  warstw systemu, wyjaśnię bardziej szczegółowo po co wprowadzono trójwarstwowy model aplikacji wspomniany w poprzednim poście. Otóż dzięki separacji kodu na warstwy nasza architektura stanie się elastyczniejsza. Model umożliwi nam m.in.:

  1. Przenośność. Kolejne warstwy będą mogły być rozmieszczane na różnych platformach sprzętowych. W każde chwili będziemy mogli np. przenieść warstwę biznesową na zewnętrzny serwer, bez konieczności modyfikowania kodu, Dzięki niezależności warstwy prezentacji od warstwy biznesowej stanie się to bardzo proste w realizacji.
  2. Redukcja zbędnych powiązań między klasami – model definiuje, że warstwa może odwoływać się wyłącznie do warstw znajdujących się bezpośrednio pod nią. Warstwa prezentacji może wywoływać wyłącznie warstwę biznesową(lub usług w przypadku modelu rozszerzonego), warstwa biznesowa z kolei powinna odwoływać się tylko do warstwy dostępu do danych. Sytuacja, w której warstwa biznesowa posiada referencję do warstwy prezentacji jest niedopuszczalna.
  3. Skalowalność – dzięki rozdzieleniu kodu łatwo można zaimplementować np. mechanizm load balancing(równoważenie obciążenia).
  4. Łatwiejsze testy integracyjne. O metodykach testów integracyjnych w szczegółach napiszę innym razem. Ogólnie chodzi o przetestowanie połączeń między modułami. W przypadku gdy mamy podzielony system na warstwy, biblioteki łatwiej jest nam zdefiniować te połączenia. Ponadto każdą warstwę można zastępować tzw. obiektem mock  (obiekt naśladujący prawdziwy obiekt, ale tak naprawdę nie zawierający żadnej logiki – zwraca na sztywno określone wyniki).

Kolejną istotną sprawą w modelu trójwarstwowym jest zdefiniowanie połączeń między warstwami czyli określenie w jaki sposób warstwy będą komunikować się ze sobą. W najprostszym przypadku będzie to po prostu lokalne wywołanie klas. W systemach enterprise, często moduły są rozproszone i istnieje wiele sposób komunikacji. O to schemat rozszerzony o kilka możliwych protokołów komunikacji:

image

TDS to protokół komunikacji z bazą danych (Tabular Data Stream). Zdefiniowanie protokołów zależy już od konkretnej aplikacji. Powyższy rysunek to tylko prosty przykład. Ważniejszy jednak jest kierunek strzałek ponieważ określa on referencję między warstwami – warstwa może wywoływać kod wyłącznie warstwy znajdującej się bezpośrednio pod nią.

W następnym poście zamierzam zacząć opisywanie warstwy biznesowej. Prawdopodobnie będzie to wstęp teoretyczny do tej warstwy oraz pierwszy wzorzec projektowy – skrypt transakcji. Zapraszam!

Czym jest oprogramowanie typu enterprise? Trójwarstwowy model aplikacji

Dziś przyszedł czas na poruszenie tematu architektury aplikacji typu enterprise. Planuje napisać cykl postów m.in. o różnych wzorcach projektowych wykorzystywanych do budowy kolejnych warstw systemu. Zacznę od totalnych podstaw, które mają na celu wyjaśnienie z czego tak naprawdę powinna się składać solidna aplikacja. Przedstawię również kilka prostych zasad inżynierii oprogramowania mających na celu usprawnienie pisania elastycznego kodu.

Zacznijmy od określenia czym jest aplikacja enterprise. Według niektórych definicji jest to oprogramowanie rozwiązujące problem biznesowy (ERM, CRM itp). Definicja według mnie jest  zbyt  ogólna i niewyjaśniająca znaczenia z punktu technicznego. Dla architekta \ programisty oprogramowanie enterprise powinno być kojarzone z następującymi cechami:

  1. Skalowalność – system powinien być przygotowany na wzrost liczby użytkowników zarówno pod względem architektonicznym(rozszerzalność kodu) jak i wydajnościowym(np. load balancing),
  2. Bezpieczeństwo,
  3. Modularność,
  4. Możliwość dostarczania modułów przez różnych dostawców(inna firma piszę CRM a jeszcze inna system sprzedaży),
  5. Jawne oddzielenie logiki od spraw technicznych(interfejs nie zna specyfiki rozwiązywanego problemu ani źródła danych),
  6. Wysoce skomplikowane systemy – implementowane i wdrążane przez lata,
  7. Systemy rozproszone.

Z pewnością do powyższych cech można jeszcze dodać wiele elementów. Uogólniając aplikacjami klasy enterprise są programy solidne, elastyczne i otwarte na wszelkie modyfikacje.

Następne pytanie brzmi, jak zaprojektować taką aplikację? Część odpowiedzi będzie znajdować sie w kolejnych postach. Popatrzmy jednak w tej chwili na wskazany problem bardziej ogólnie. Zdefiniujmy warstwy(części) na które składa się każdy system enterprise. W podejściu Rapid Application Development(RAD), promowanym przez nowoczesne środowiska programistyczne sprawa jest prosta. Pisząc prostą aplikacje np. w Visual Studio tworzymy nowe okno i dodajemy kod obsługi bezpośrednio w pliku źródłowym okna. W poważniejszych aplikacjach niezbędne okazuje się oddzielenie od siebie przynajmniej trzech warstw kodu:

  1. Warstwy prezentacji,
  2. Warstwy biznesowej,
  3. Warstwy dostępu do danych.

Dokładny opis warstw znajdzie się w przyszłych postach – w tej chwili wyjaśnię tylko ogólnikowo. Warstwa prezentacji to wszystko to co widzimy  czyli interfejs użytkownika(GUI). Warstwa biznesowa jest rdzeniem aplikacji i odpowiada za rozwiązywanie problemów dla których została stworzona aplikacja. Czasami rozdziela się warstwę biznesową dodatkowo na warstwę usług aby uniezależnić kwestie technologiczne od implementacji tej warstwy . Warstwa dostępu do danych odpowiada za fizyczny zapis danych do dowolnej bazy danych. Poniżej przedstawiam warstwy wraz z kilkoma wzorcami projektowymi które można wykorzystać:

image

Spora część wzorców będzie omawiana na bieżąco w przyszłych postach zatem zachęcam do czytania.