Struktura „klasycznej” aplikacji trójwarstwowej

Kwiecień 28, 2009

Pisząc ten wpis miałem na celu porównać architekturę opartą o metodykę Domain Driven Design, z tą, która najczęściej jest stosowana przynajmniej w projektach dot netowych, czyli architekturą opartą od Data Driven Design. Ze względu na ogrom informacji, które byłyby zawarte w tym w wpisie, stwierdziłem, że lepiej będzie jeżeli podzielę to na dwa wpisy. Drugi pojawi się wkrótce – obiecuję :) .

Na wstępie chciałbym również zwrócić uwagę na to, że Rafał Barszczewski w jednym ze swoich wpisów na swoim blogu poruszył podobny temat. Mam nadzieję, iż poniższy tekst będzie uzupełnieniem informacji przedstawionych przez Rafała, a nie ich powieleniem.

Zakładam, że każdy z Was jest świadom tego, że pisanie całej aplikacji w Code Behind nie doprowadzi do wielkich sukcesów projektu. Jeżeli jesteście innego zdania, nie czytajcie tego wpisu, bo prawdopodobnie nie macie do czynienia z aplikacjami biznesowymi.

Jednym z najbardziej znanych pojęć w świecie architektury oprogramowania jest luźne sprzężenie. Czy nie każdy z nas zastanawiał się nad tym, dlaczego luźne sprzężenie jest tak ważne? W każdym razie ja się zastanawiałem i to tak długo, dopóki nie musiałem zmienić prawie całej aplikacji tylko dlatego, że chciałem wprowadzić proste zmiany. Całe szczęście była to dla mnie nauczka i od tego czasu luźne sprzężenie jest najbardziej krytycznym niefunkcjonalnym wymaganiem w tworzonych przeze mnie aplikacjach.

Po tym jak zdałem sobie sprawę z tego jak ważne jest luźne sprzężenie, zacząłem się zastanawiać nad tym, co zmienić w moim podejściu do pisania kodu tak, bym nigdy więcej nie natrafił na podobne problemy. Dzisiaj już wiem, że nie ma na to złotego środka i wszystko zależy od rozważanego przypadku. Mimo to uważam, że jest pewne rozwiązanie, które jest jednym z pierwszych kroków w celu uzyskania mniej sprzężonej architektury aplikacji. Rozwiązaniem tym jest podział aplikacji na warstwy logiczne.

Większość z Was zapewne zna pojęcie warstw logicznych, ale założę się, że nie każdy jest świadom, czym one tak naprawdę są. Postaram się szybko to wytłumaczyć.

Warstwy logiczne są:

  • Drugim najbardziej używanym pojęciem po luźnym sprzężeniu, jeżeli chodzi o architekturę oprogramowania:).
  • Wzorcem architektonicznym pozwalającym aplikację podzielić na wiele grup podzadań (warstwy), z których każda grupa reprezentuję odpowiedni poziom abstrakcji. Grupy te mogą, a wręcz powinny, być od siebie zależne, przy czym zależności te muszą być jednokierunkowe. Poprzez jednokierunkową zależność należy rozumieć to, że grupa podzadań A, która jest zależna od grupy B, nie powinna być wykorzystywana przez grupę B. Grupa podzadań B nawet nie powinna mieć żadnego rodzaju świadomości o grupie A. Jedyne zależności jakie może mieć grupa B, to te do grup, które w hierarchii są poniżej jej.

     

Jeżeli chodzi o warstwy logiczne, to najczęściej znane są one ze względu na trójwarstwową architekturę aplikacji. Architektura ta zakłada, że aplikacja składa się z warstwy prezentacji zależnej od warstwy logiki biznesowej, która z kolei jest zależna od warstwy dostępu do danych.

Architektura trójwarstwowa w dzisiejszych czasach jest na tyle wszechobecna, że niemalże każdy projekt biznesowy, z którym miałem do czynienia z niej korzystał. Co nie znaczy, że jest ona złotym środkiem na wszystko. Głównym problemem związanym z tym podejściem jest to, że prowadzi ono często do tego, że każda warstwa reprezentuje jeden typ klasy.

  • Warstwa prezentacji składa się wyłącznie z formularzy (Web, Desktop, etc.), w których zawarta jest logika prezentacji(Code Behind)
  • Warstwa logiki biznesowej składa się z klas posiadających metody, za którymi kryje się odpowiednia logika biznesową. Metody te są najczęściej wywoływane bezpośrednio z poziomu warstwy prezentacji. Tego typu rozwiązanie znane jest jako wzorzec Transaction Script.
  • Warstwa dostępu do danych zarządza dostępem do źródła danych. Najczęściej źródłem jest baza danych, ale zdarzają się również inne przypadki takie jak plik, czy inne aplikacje. W przypadku bazy danych, warstwa ta najczęściej się składa z metod, które wywołują odpowiednie zapytania SQL lub procedury składowane. Dzięki wprowadzeniu tego typu klas, jesteśmy w stanie wprowadzić luźne sprzężenie pomiędzy logiką biznesową aplikacji a źródłem danych, które aplikacja wykorzystuje.

W tym powyższym podejściu brakuje mi jednak jednej rzeczy, mianowicie obiektów domenowych. Obiekty te najczęściej znajdują swoje miejsce w architekturze trójwarstwowej, ale trudno je przyporządkować do konkretnej warstwy. Teoretycznie powinny być traktowane jako część warstwy logiki biznesowej. Jednak nie jest tak dlatego, że są one współdzielone przez wszystkie warstwy i służą jedynie jako medium komunikacji pomiędzy tymi warstwami. Należy zwrócić uwagę, że obiekty te posiadają jedynie stan (właściwości), a to znaczy, że są one typowym przykładem anemicznych obiektów domenowych.

Żeby całość lepiej zrozumieć posłużę się przykładem.

Załóżmy, że mamy aplikację, w której użytkownik posiada dostęp do przeglądania określonych zasobów. W celu przejrzenia zasobu wysyła zapytanie wraz z informacją identyfikującą określony zasób. Zapytanie to jest przechwytywane przez warstwę prezentacji i zostaje przekierowane do warstwy logiki biznesowej, gdzie z kolei jest sprawdzane, czy dany użytkownik może dokonać tego typu operacji. Następnie zostaje wykonywane zapytanie do warstwy dostępu do danych, w celu zwrócenia informacji o określonym zasobie. Zapytanie te tłumaczone jest na przykład na język SQL i zostaje wykonane zapytanie do bazy danych. Zwrócone informacje z bazy tłumaczone są do postaci obiektu domenowego, który zostaje zwrócony z warstwy dostępu do danych do warstwy logiki biznesowej. Z kolei ta sprawdza czy dany użytkownik, powinien mieć dostęp do tego obiektu domenowego. W przypadku gdy wszystko będzie się zgadzało, obiekt ten zostanie przekazany do warstwy prezentacji, w ramach której wygenerowany zostanie odpowiedni widok przedstawiający informacje zawarte w tym obiekcie domenowym.

Wszystko jest “fajne”, ale czy tego typu architektura aplikacji nadal powinna być nazywana trójwarstwową?

Architektura ta jest wygodna w przypadku prostych aplikacji, zwanych często “Data Driven”, dlatego, że aplikacje te nie posiadają skomplikowanych reguł biznesowych. Głównym problemem wyżej przedstawionej architektury jest duplikacja kodu z logiką biznesową, dlatego że stosowanie Transaction Script zmusza nas do definiowania osobnego kodu dla każdej poszczególnej operacji. Dlatego też w przypadku, gdy celem aplikacji jest realizacja wielu skomplikowanych reguł biznesowych, należy się zastanowić nad architekturą zgodną z założeniami Domain Driven Design.

Entry Filed under: Uncategorized. .

3 Comments Add your own

  • 1. dotnetomaniak.pl  |  Maj 6, 2009 at 1:06 pm

    Struktura „klasycznej” aplikacji trójwarstwowej « !FrAgile Thinking…

    Dziękujemy za publikację – Trackback z dotnetomaniak.pl…

    Odpowiedz
  • 2. nandrew  |  Maj 17, 2009 at 7:26 am

    Próbujesz tutaj wyszukać wady klasycznej architektury trójwarstwowej, jednocześnie przedstawiasz jej zły schemat. W trójwarstwowym systemie warstwa BLL/Domain jest “na samej górze”, nie ma zależności od innych warstw. Wszystkie obiekty serwisowe (ewentualnie transaction script) oraz obiekty domenowe (ewentualnie encje) są w tej warstwie – bez rozdzielana na BLL i osobne obiekty domenowe.

    Pozostałe warstwy są zależne tylko od BLL/Domain. Gdy potrzebna jest osobna relacja stosuje się DIP (Dependency Inversion Principle) i wszystko gra…

    Co więcej, te zasady są o wiele starsze niż pojęcie “Domain Driven-Design”, które ostatnio jest tak popularne.

    W sumie nie wiem co będzie w drugiej części posta, ale mam nadzieję, że nie będziesz sugerował, że “luźne wiązanie” (loose coupling) jest wykorzystywane tylko w DomainDrivenDesign, a w “zwykłym programowaniu” (standardowych 3/4/5 warstwowych aplikacjach) już nie.

    Odpowiedz
  • 3. jenrom  |  Maj 19, 2009 at 10:28 am

    @nandrew

    Nie twierdzę, że DDD wprowadza luźne sprzężenie i napewno nikogo nie będe do tego przekonywać. Wprowadzenie warstw w każdym przypadku zmniejsza sprzężenie, dlatego, że warstwy wymuszają jednokierunkową komunikację i definują spójne ze sobą elementy aplikacji. Dlatego też nie śmiem twierdzić, że wyżej przedstawione rozwiązanie nie jest luźno sprzężone.

    Opisany przez ciebie schemat architektury w dużej mierze pokrywa się z założeniami DDD i w pełni z tobą się zgodze, że w większosći przypadków rozwiązanie to będzie się lepiej sprawowało. Mimo to często spotykam się z stosowaniem architektury przedstawionej w poście, dlatego też nazwałem ją klasyczną. Z kontekstu twojego komentarza uważam teraz że wybrana przeze mnie nazwa -”klasycznej” architektury jest z leksza chybiona.

    “Co więcej, te zasady są o wiele starsze niż pojęcie “Domain Driven-Design”, które ostatnio jest tak popularne.”

    Zgadzam się z tobą. Warto zwrócić uwagę, że większość rozwiązań w DDD to nic nowego, tylko utrwalenie wcześniej już znanych aspektów programowania obiektowego.

    Odpowiedz

Leave a Comment

Required

Required, hidden

Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackback this post  |  Subscribe to the comments via RSS Feed


Aktualnie czytam

  • Enterprise Integration Patterns

Tagi

About Agile ALT.NET ASP.NET MVC DDD Domain Driven Design Domain Model Exceptions Front Controller Logika biznesowa NHibernate OOP ORM Pair Programming Podstawy Prezentacja Projekty Informatyczne Routing SQL TDD Wroc.NET Wzorce XP

Dodatki

Blogroll

Znajomi

Najnowsze komentarze

zajefajnyx on Czy ty też nadużywasz procedur…
jenrom on Logowanie i obsługa wyjątków …
am on Fluent Nhibernate Rocks!!…
Jarek on Logowanie i obsługa wyjątków …
jenrom on Logowanie i obsługa wyjątków …

Archiwa