RSS

Blog

Projektowanie oprogramowania w świecie .NET. Agile, OOP, wzorce projektowe, DDD, ORM, TDD, AOP i inne...

Wzorce organizacji warstwy logiki

19 listopada 2008 Odsłon: 2831

W poprzedniej części przedstawiłem metody ogólnego podziału systemu na warstwy logiczne i fizyczne. Teraz czas zająć się problemem, który poprzednio tylko zarysowałem, czyli: jak wpasować logikę biznesową w strukturę warstw logicznych oraz jak zorganizować przepływ sterowania, żeby mogła być ona niezależna od infrastrukturalnej części systemu?

Przede wszystkim zakładamy, że logika biznesowa ma znaleźć się w całości (albo chociaż w znaczącej części) w warstwie pośredniej. Zatem zgodnie z tym co napisałem poprzednio, warstwa logiki powinna teraz zawierać trzy różne rodzaje elementów:

  • Model. Dane "biznesowe" w jakiejś reprezentacji programowej dla wygody przetwarzania. Mogą to być klasy, ale też np. struktury albo DataSety (na wartościowanie ich w kontekście DDD przyjdzie czas nieco później).
  • Logika biznesowa. Operacje przeprowadzane na modelu.
  • Logika aplikacji. Obsługa wyjątków, autoryzacja, kontrola transakcji, koordynacja pobierania danych z warstw niższych itd.

Elementy warstwy pośredniej. Jak je zorganizować?

Wszystkie te elementy są niezbędne i każdy jeden nie ma sensu bez pozostałych. Jak zatem najlepiej je ze sobą powiązać, jak zorganizować przepływ sterowania i komunikację między nimi? Jest na to znanych co najmniej kilka sposobów. Martin Fowler w książce "Patterns of Enterprise Application Architecture" wymienia trzy najważniejsze z nich: Transaction Script, Table Module oraz Domain Model.

Transaction Script

To prawdopodobnie najpopularniejszy wzorzec, szczególnie w przypadku systemów legacy i zespołów doświadczonych, przyzwyczajonych do tradycyjnych metod. Zgodnie z Transaction Script, cała warstwa logiki powinna zostać zorganizowana w procedury, które z kolei mogą być pogrupowane w klasy. Poszczególne procedury odpowiadają przypadkom użycia systemu. Realizują one całe przetwarzanie: pobierają dane z warstwy niższej, operują na modelu w celu wykonania zadanego scenariusza biznesowego, a na koniec zapisują zmodyfikowane dane lub zwracają je do warstw zewnętrznych.

Transaction Script z lotu ptaka

Przykładowo, implementacja przypadku użycia pt. "Klient otrzymuje specjalną zniżkę, jeżeli wartości jego zamówień przekroczyły graniczną kwotę" według wzorca Transaction Script mogłaby wyglądać następująco:

public class CustomerService
{
    public void RewardCustomer(int id)
    {
        DataTable customers = DB.GetCustomer(id);
        DataRow c = customers[0];
 
        if ((decimal)c["TotalOrderAmount"] > 1000.0m)
        {
            DataTable orders = DB.GetCustomerOrders(id);
 
            for (int i = 0; i < orders.Rows.Count; i++)
            {
                DB.UpdateOrder((int)orders[i]["Id"],
                    (decimal)orders[i]["Price"], 0.1f);
            }
        }
    }
}

Zaprezentowany kod wcale nie jest skrajnym przypadkiem, choć na pewno możnaby go upiększyć, gdyby użyto np. wygenerowanych klas i Entity Framework do pobierania danych, jak w poniższym przypadku:

Application layer, podejście proceduralne. Unikać

Nie zmienia to jednak istoty rzeczy: proceduralnego (nieobiektowego) podejścia, traktowania obiektów jedynie jako kontenery na dane, oddzielenia reguł biznesowych od modelu. Transaction Script charakteryzuje się niską elastycznością, duplikacją kodu i znacznie utrudnionym testowaniem. Do tego dochodzą problemy "ideologiczne": programista musi nadmiernie koncentrować się na infrastrukturze, jest oderwany od istoty zagadnienia. Dla tak zorganizowanego kodu nie można zastosować wzorców projektowych, czy choćby prostego polimorfizmu. W praktyce oznacza to cofnięcie o kilkanaście lat pod względem metod programowania.

Gwoli sprawiedliwości należy dodać, że takie podejście nie powoduje automatycznej klęski projektu. Z pewnością ma ono swoje zalety, jak choćby to, że łatwo jest przenieść do takiej struktury starszy kod (zwłaszcza sprzed czasów .NET). Nie wymaga też wielkich umiejętności OOP od programistów, co może mieć wcale niemałe znaczenie ("programowanie obiektowe jest zawsze trudniejsze, niż ci się wydaje"). Za to tzw. programiści bazodanowi poczują się jak ryby w wodzie. Jednak w dłuższej perspektywie tak budowane systemy cierpią na różne przypadłości, opisywane już przeze mnie wyżej - i dlatego poszukujemy czegoś lepszego.

Table Module

Wzorzec podobny do poprzedniego, dlatego opiszę go jedynie skrótowo. Różni się głównie tym, że poszczególne klasy odpowiadają tabelom w bazie danych, jest zatem nieco bardziej zorganizowany niż zorientowany na procedury Transaction Script. Dzięki temu można częściowo uniknąć np. duplikacji kodu (metody jednego modułu są reużywalne dla innych), ale w dłuższej perspektywie Table Module cierpi na podobne problemy co poprzednik. Sprawdza się najlepiej w niedużych projektach, np. serwisach internetowych, o prostej logice biznesowej, sprowadzającej się zwykle do walidacji.

Domain Model

Przykładowy, prosty model dziedziny

Nazwanie Domain Model wzorcem jest trochę naciągane, gdyż oznacza on po prostu podejście polegające na tym, że staramy się odzwierciedlić dziedzinę problemu biznesowego w sposób maksymalnie zgodny z technikami programowania obiektowego. Programista może skupić się w całości na implementacji logiki biznesowej, która znajduje się tam, gdzie jej miejsce, czyli w klasach modelu. Wzorce projektowe są szeroko zastosowane, podobnie jak inne techniki OOP. "Procedury" odpowiadające przypadkom użycia w warstwie pośredniej przygotowują jedynie niezbędną infrastrukturę, delegując wszelkie operacje związane z dziedziną do klas biznesowych. Te z kolei są niezależne od wszelkich rozwiązań technicznych zastosowanych w projekcie - nie odwołują się do żadnych frameworków, kontenerów czy DAL. Kod z poprzedniego przykładu upraszcza się do postaci:

Sytuacja jak wcześniej, podejście obiektowe

Korzyści powinny wydawać się jasne: zwiększona odporność na zmiany, możliwe skuteczne testowanie jednostkowe, przedłużona żywotność projektu, większy porządek w kodzie, bardziej efektywne wykorzystanie czasu i umiejętności programistów. Domain Model błyszczy najbardziej w projektach o skomplikowanej logice biznesowej. Martin Fowler zauważa jednak, że osoby, które nauczyły się tworzyć aplikacje w taki sposób, nie chcą już powracać do poprzednich metod i dążą do stosowania Domain Model we wszystkich swoich projektach.

Podsumowanie

W porządku, ale skoro jest to takie dobre i ma tyle zalet, to dlaczego większość wybiera jednak podejścia alternatywne? Czy są to ignoranci odporni na argumenty? A może nie zdają sobie sprawy z wad, którymi te metody są obarczone? Otóż nie. Głównym powodem, dla którego Domain Model - wzorzec tak stary, jak całe programowanie obiektowe - nie jest stosowany w aplikacjach biznesowych jest to, że jego połączenie z resztą aplikacji jest - uwaga - trudne. Trudne zarówno od strony technicznej, jak i konceptualnej. Do niedawna programiści technologii Microsoft nie mieli do tego odpowiednich narzędzi, ale teraz są już dostępne porządne O/R mappery, frameworki, narzędzia do AOP czy TDD (w kolejnych częściach będę przyglądał się im bliżej). Z kolei od strony konceptualnej problem leży w odpowiedniej strukturze modelu dziedziny.

I tutaj właśnie wchodzi do gry Domain-Driven Design, proponując konkretne, usystematyzowane podejście do konstruowania modelu. Postaram się przedstawić je w kolejnej części z serii.

Komentarze

Aktualizacja danych...
rafalb
2009-08-26 07:17

Może jeszcze będzie, jak uporam się z bieżącymi sprawami (studia już za mną, przeprowadzka w trakcie, jeszcze ślub itd.).

cnrd
2009-08-20 06:01

szkoda, że nie ma kontynuacji...

fil
2008-12-02 21:40

interesujące, oby tak dalej...

Aktualizacja danych...

Kontakt CV

Ja

Rafał Barszczewski
rb07 at interia.pl
gg: 1242248

Sonda

Co sądzisz na temat wykorzystania Ajaksa na stronach internetowych?



Aktualizacja danych...

RSS

20 września 2008

Jak widać, spore zmiany. Zaimplementowałem najważniejsze funkcjonalności, które powinien mieć każdy silnik blogów, a których do tej pory u mnie brakowało. Chodzi mi tu przede wszystkim o tagi i RSS. Poza tym strona startowa bloga będzie teraz wyświetlać najnowszego posta, a na panelu bocznym pojawiła się lista ostatnich wpisów.

Ponadto, postanowiłem wznowić pisanie postów (a właściwie je rozpocząć - bo na dobrą sprawę nigdy poważnie nie zacząłem). W końcu w jakimś celu to wszystko zaprogramowałem ;) Tematyka, którą będę chciał w najbliższym czasie poruszyć, obejmuje zagadnienia związane z projektowaniem i testowaniem aplikacji oraz metodykami i narzędziami, które te procesy wspomagają. Zapewne najwięcej będzie o Domain-Driven Design i Test-Driven Development, choć spróbuję podejść do tych tematów bardzo pragmatycznie (technicznie). Mam też plan, żeby w miarę pisania kolejnych części powstawała konkretna, przykładowa aplikacja, która mogłaby posłużyć jako case-study. Co z tego wszystkiego wyjdzie - zobaczymy niebawem.

13 kwietnia 2007

Nie doszedł (na razie) żaden nowy wpis, ale za to przeorganizowałem trochę ten dział. Na głównej podstronie znajdują się teraz same nagłówki wpisów, a całość możemy przeczytać (i skomentować) po przejściu do szczegółów.

21 października 2006

Pierwszy, historyczny wpis w moim blogu. Od tej pory postaram się regularnie dodawać nowe posty. A już niedługo - moje wrażenia z Microsoft Technology Summit (24-25.10).