Artykuł z magazynu ITwizArchitektura ITPolecane tematy

Wzorce, standardy, frameworki, metodyki…

Gdy komuś zadaje się pytanie, czy je zna i stosuje – zwykle odpowiada twierdząco. Kolejne pytanie – dotyczące opisania korzyści z zastosowania wzorców, standardów, frameworków i metodyk – najlepiej praktycznych, a nie wyrecytowania sloganów, wymaga chwili zastanowienia.

Wzorce, standardy, frameworki, metodyki…

Wskazanie sytuacji, w których warto stosować mniej, lub bardziej gotowe wzorce, a także kiedy należy ich unikać, oraz przemyśleń na podstawie dotychczasowych doświadczeń – niekoniecznie osobistych, ale opartych na czymś więcej niż powszechnie powielane, mniej lub bardziej trafne mity – wprowadza niektóre osoby w zakłopotanie. Podjęcie słusznej drogi, w cyklu życia produktu IT, wymaga dodatkowo – o ile nie przede wszystkim – wzięcia pod uwagę specyfiki szeroko pojętego otoczenia – w znaczeniu biznesowym, zasobowym i technicznym. Obawiam się, że w większości przypadków – nie istnieje generyczny zbiór reguł, który pozwalałby funkcyjnie wyznaczyć idealne, dla wszystkich, podejście. Gdyby było to możliwe – proces decyzyjny można byłoby zautomatyzować, albo przynajmniej opracować uniwersalny algorytm, zdejmujący z organizacji ciężar odpowiedzialności za tego typu decyzje. Tak się, jednak, nie stało.

Tekst pochodzi z magazynu ITwiz 8/2014.

Dlaczego warto stosować wzorce

Począwszy od warstwy kodu, przez różnego rodzaju modele, a na strategii korporacji kończąc – właściwe użycie (bądź dostosowanie) gotowych szkieletów „z półki” w sposób mechaniczny, a jednocześnie optymalny bywa niełatwe. Istnieją zbiory standardów, rekomendacji Itp., ale w przypadku ślepego ich stosowania można przynieść więcej szkody niż pożytku, a w skrajnym przypadku doprowadzić przedsięwzięcie do upadku. Czy należy więc unikać standaryzacji i metodyk? Absolutnie nie. Niosą one spory bagaż doświadczeń, pomysłów, oraz sposobów na to, aby nie wynajdować koła od zera. Pisząc nieco kolokwialnie – cała sztuczka polega na tym, by owego koła nie stosować tam, gdzie potrzebny jest kwadrat.

Podjęcie słusznej drogi, w cyklu życia produktu IT, wymaga dodatkowo – o ile nie przede wszystkim – wzięcia pod uwagę specyfiki szeroko pojętego otoczenia w znaczeniu biznesowym, zasobowym i technicznym. Prawdopodobnie w większości przypadków  nie istnieje generyczny zbiór reguł, który pozwalałby funkcyjnie wyznaczyć idealne, dla wszystkich, podejście. Gdyby było to możliwe – proces decyzyjny można byłoby zautomatyzować, albo przynajmniej opracować uniwersalny algorytm, zdejmujący z organizacji ciężar odpowiedzialności za tego typu decyzje.

Motywacji do stosowania sprawdzonych rozwiązań może być wiele. Niektóre z nich to pokusa uporządkowania danego obszaru organizacji, przez ubranie procesów w przewidywalne ramy, poddające się rozsądnym (!) pomiarom, a także skrócenie czasu niezbędnego do wypracowania optymalnych produktów IT oraz wszelkich innych, od nich zależnych. Często pomijaną korzyścią ze stosowania wzorców jest czas wykraczający poza ramy życia przedsięwzięcia. Zbyt rzadko rozważana jest kwestia tego, czy, przykładowo, zupełnie nowy zespół będzie w stanie sprawnie obracać się w rzeczywistości wykreowanej bieżącymi inicjatywami.

Czy nowo zatrudniony programista wpadnie w chaszcze „przedziwnego” kodu, lub jego fragmenty – dzięki zastosowaniu sprawdzonych i publicznie dostępnych rozwiązań – będą jednak same się tłumaczyły, nawet przy ubogiej dokumentacji, lub wręcz jej braku (co naturalnie stanowi zamach na przyjęte teoretyczne standardy, ale stanowi smutną rzeczywistość wielu firm). Czy architekt spoza organizacji będzie mógł przyjąć, że integracja międzysystemowa jest realizowana zgodnie z powszechnymi normami i realizując nowe inicjatywy, nie będzie przeznaczać cennego czasu na poznawanie, mówiąc delikatnie, tzw. rozwiązań autorskich? Czy wysokopoziomowe szacowanie nakładów prac, związanych z nowymi inicjatywami, będzie mogło założyć ich realizację w przyjaznym i przewidywalnym środowisku, czy może ryzyka niespodzianek są tak duże, że większość budżetu należy przeznaczyć na ich ewentualną kompensację? Konsolidacja środowisk, wykonalna na poziomie górnolotnych prezentacji, będzie technicznie wykonalna, jeśli są one oparte na różnym mianowniku norm? W dużym uproszczeniu, na te wszystkie problemy odpowiedzią jest stosowanie standardów.

(Anty)przykłady stosowania wzorców

Zatem kiedy, jednak, będą one stanowiły wyżej wspomniane koło, zamiast oczekiwanego kwadratu? Postaram się to pokazać na kilku (anty)przykładach, gdyż – jak zwykło się mówić – najlepsza nauka płynie z błędów, a nie słodkich i idealnych podręcznikowych sytuacji, które mają zastosowanie w sterylnych, laboratoryjnych warunkach, a nie realnym życiu. Chciałbym jednocześnie nadmienić, że wspomniane przypadki nie stanowią ataku na przytoczone wzorce, a jedynie pokazują ich – nie do końca optymalne – użycie.

Każda osoba, jakkolwiek związana z programowaniem obiektowym, prędzej czy później spotyka się – mniej lub bardziej świadomie – z pomysłami zaprezentowanymi przez tzw. Gang Czterech. Od czasu pierwszej publikacji minęło sporo czasu, idee doczekały się realizacji w każdym liczącym się języku programowania. Owe implementacje, z racji różnić syntaktycznych, między językami mogą nieco od siebie odbiegać, jednak co do samej zasady rozwiązania, na poziomie obiektowym są niemalże identyczne. Wraz ze wzorcami przyszedł ich słownik i obecnie, gdy ktoś zajrzy w cudzy kod, od razu zorientuje się, po samej nazwie – o ile została użyta zgodnie z pierwotnym zastosowaniem – czym jest Fasada, zarówno na poziomie programistycznej klasy, jak i integracyjnego bloku architektonicznego.

Zastosowanie wzorców usprawiedliwia przeciętne IDE (Integrated Development Environment). Nawet bowiem jeśli pojedyncze kliknięcie nie zaprowadzi nas automatycznie we właściwy fragment kodu, to będziemy wiedzieli, gdzie się spodziewać interesującej nas implementacji. Same zalety. Jakie jednak niebezpieczeństwo czyha na deweloperów, zwłaszcza tych mniej doświadczonych? Tak, jak z większością wzorców, nadmierna ekscytacja nowo poznanym tematem i próba jego wdrażania za wszelką cenę. Nawet tam, gdzie nie jest to konieczne. Prowadzi to do występowania efektu tzw. over-engineeringu, czyli de facto nadmiernej komplikacji rozwiązania tam, gdzie nie jest to potrzebne, bądź korzyści z jego stosowania są niewspółmiernie niskie w stosunku do wymaganego nakładu pracy. Poza tym, zbyt gotyckie rozwiązania, mimo że mogą robić wrażenie, czy to ilością elementów diagramu klas, czy rozległością kodu – są zwykle trudniejsze w utrzymaniu, i wbrew pozorom, także rozwoju. Nie chodzi tylko o stosowanie, bądź nie, wzorców GoF, ale także np. pryncypiów programowania obiektowego, konfigurowalności rozwiązania itp.

Znany jest przykład antywzorcowej implementacji hello world, której wydruk ledwo mieści się na kilku stronach. Tymczasem, biorąc pod uwagę główną funkcjonalność, jaką hello world ma dostarczać – szybkie wyświetlenie dwóch wyrazów, zazwyczaj w celu przetestowania nowo poznawanego języka lub kompilatora – wystarczy, w zależności od dialektu, jedna do kilku linijek kodu. Wspomniana implementacja „negatywna” została stworzona jako karykatura, jednakże zdarzyło się mi oglądać wdrożony produkcyjnie kod, dostarczony przez rozsądny zespół, gdzie dla najprostszych funkcjonalności tworzono rozwiązanie, takie, jakby twórcy otrzymywali premię proporcjonalnie wysoką do zagęszczenia wzorców na segment kodu. Na pierwszy rzut oka wyglądało to całkiem solidnie i atrakcyjnie, jednakże gdy wzięło się pod uwagę realnie dostarczane przez rozwiązanie funkcjonalności, szybko można było zauważyć jego nadmierne „przegadanie”.

Decyzje architektoniczne na poziomie relacji międzysystemowych oraz ich standaryzacja mają jeszcze większy wpływ na to, jak będzie funkcjonowało środowisko, którego elementami zajmują się często niezwiązane ze sobą zespoły. Efekt ustaleń charakteryzuje się często dużą bezwładnością i ma spore przełożenie na budżet. Owe ustalenia muszą uwzględniać stan zastany, a także pozostawać w zgodzie ze strategią organizacji.

Kiedy najlepiej korzystać ze wzorców GoF? Gdy faktycznie potrafią odpowiedzieć rozwiązaniem na napotkany temat, a nie sztucznie go tworzyć tylko po to, aby „elegancko” go załatwić. Czy to jedyna zasada? Oczywiście, nie. Wystarczy wspomnieć o implementacji – np. w Javie, korzystając obecnie z dostępnych frameworków typu Enterprise JavaBeans (EJB) czy Spring. Choć klasyczna implementacja Singletona ze sztuczką w postaci sztucznego konstruktora jest prosta. Można skorzystać z jednowyrazowej anotacji i uzyskać skutek niemal identyczny jeszcze szybciej.

Skoro już wspomniałem o dwóch frameworkach – przypomniał mi się przypadek rozwiązania IT, z okresu, gdy najnowszą dostępną wersją była EJB 2. Wersja ta, między innymi, przez swój stopień skomplikowania, jest dość niewygodna w użyciu, co zapewnia, przede wszystkim, całkiem przyzwoite przychody wysoce specjalizowanym fachowcom. Żartuję. Stosowanie pieszczotliwie nazywanych korpo-ziarenek ma spory zestaw zastosowań w obszarach, gdzie sprawdzają się idealnie, spełniając obiecane korzyści – m.in. poprzez wymuszenie porządku warstw wewnątrz aplikacji, oraz dostarczając wielu sposobów na przepływ danych między nimi. Niestety – przypadek, który mam na myśli, nieco rozminął się z optymalnością. Była to prosta aplikacja z GUI wystawionym do internetu, bez ograniczeń dostępu. Zdecydowano się w niej na użycie EJB z beanami zdalnymi. Cała logika biznesowa tam zawarta sprowadzała się do kilku trywialnych operacji matematycznych. Co realnie przyniósł tak wykorzystany framework? Większe obawy o wydajność (niepotrzebna dodatkowa zdalna warstwa wewnątrz aplikacji), wydłużony czas implementacji oraz trudniejsze utrzymanie. W zakresie wydajności można, oczywiście, podnosić wsparcie skalowalności poziomej, jednakże proste testy potwierdziły, że upraszczając wewnętrzną architekturę systemu, tę samą prędkość przetwarzania zleceń można uzyskać na znacznie „chudszym” klastrze.

Konsekwencje złego zastosowania wzorców

Decyzje architektoniczne na poziomie relacji międzysystemowych oraz ich standaryzacja mają jeszcze większy wpływ na to, jak będzie funkcjonowało środowisko, którego elementami zajmują się często niezwiązane ze sobą zespoły. Efekt ustaleń charakteryzuje się często dużą bezwładnością, i ma spore przełożenie na budżet. Owe ustalenia muszą uwzględniać stan zastany, a także pozostawać w zgodzie ze strategią organizacji. Większość tego typu prac opiera się na doświadczeniach innych oraz wzorcach. Niestety, nie zawsze bierze się pod uwagę to, że dane rozwiązanie nie przystaje do rzeczywistości docelowego środowiska. Drugi częsty błąd to realizacja wzorca w sposób wypaczający sens jego stosowania. Poniżej parę przykładów tego typu sytuacji.

Nasze systemy IT nie są SOA? To źle. Gdy będzie SOA – będzie dobrze. Skądś to znamy? Na pewno! Podejście do opierania architektury na usługach, choćby ze względu na ogólny charakter definicji, ma bardzo szerokie zastosowanie. Świadczyć może o tym choćby fakt, że od momentu powstania paradygmatu SOA w zasadzie do dziś nie ma pomysłu na coś rewolucyjnie innego. Można, oczywiście, podejmować dyskusje, czy np. architektura oparta na mikroserwisach jest czymś znacząco odmiennym (moim zdaniem nie), czy stanowi podzbiór ogólnej definicji SOA, ale to temat na osobny artykuł. Wracając do sedna sprawy, SOA sprawdza się, ale nie wszędzie.

W przypadku nieskomplikowanych środowisk sztuczne rozbijanie spójnego systemu na mniejsze, o węższej specjalizacji, niekoniecznie odniesie oczekiwany skutek. Naturalnie – należy trzymać rękę na pulsie i starać się nie dopuszczać do sytuacji powstania wielkiego, „niezarządzalnego i nierozbieralnego kombajnu”, którego najdrobniejsze niedomaganie powoduje paraliż całej firmy, ale uważam, że w wielu miejscach zbyt łatwo zapadają niefortunne decyzje o rozpraszaniu aplikacji. Co gorsza, zdarza się, że rozproszenie – nawet gdy jest mocno wskazane – następuje w taki sposób, że potencjalne zyski (np. odseparowanie od siebie poszczególnych grup funkcjonalności i możliwość zastępowania pojedynczego klocka architektonicznego bez, lub ze zminimalizowanym wpływem na resztę środowiska realnie) się nie pojawiają.  Dlaczego? Nie ze względu na wadliwe wzorce, tylko niefortunną ich realizację. Przykład? Choćby bardzo mocne powiązania między komponentami w środowisku.

Zdarzyło się mi oglądać wdrożony produkcyjnie kod, dostarczony przez rozsądny zespół, gdzie dla najprostszych funkcjonalności tworzono rozwiązanie, takie, jakby twórcy otrzymywali premię proporcjonalnie wysoką do zagęszczenia wzorców na segment kodu. Na pierwszy rzut oka wyglądało to solidnie i atrakcyjnie, jednakże gdy wzięło się pod uwagę realnie dostarczane przez rozwiązanie funkcjonalności, szybko można było zauważyć jego nadmierne „przegadanie”.

Skąd się biorą takie trudno naruszalne więzy? Czasem to błędy w projekcie modelu danych na styku systemów, zbyt mało abstrahującym od wewnętrznych struktur aplikacji. Może to być też jakaś nietypowa warstwa transportu komunikatów międzysystemowych, w najgorszym przypadku opierająca się na przeróżnych „autorskich wynalazkach”. Zdarzają się też przypadki łatania specyficznego (tj. błędnego) działania jednej aplikacji w innej. Skutkiem takiego dyfundowania systemów jest to, że prędzej czy później ich wzajemne zależności są tak silne, iż środowisko łączy ze sobą to, co najgorsze z rozwiązań monoblokowych i rozproszonych. Najdrobniejszą zmianę rozważać trzeba bowiem w kontekście wszystkich systemów, a dodatkowe nakłady na integrację pozostają wysokie.

Innym wypaczeniem jest bezkrytyczne stosowanie szyny danych ESB wszędzie, gdzie się da. Stanowi ona bardzo istotny element architektury i potrafi przynieść wiele dobrego. Choćby w wyżej wspomnianym przykładzie negatywnym. Rozsądne wprowadzenie wydzielonej warstwy integracyjnej – o ile odpowiadają za nią rozsądni i odpowiednio umocowani w organizacji ludzie – naturalnie przeciwdziała powstawaniu wyżej opisanych silnych zakleszczeń między aplikacjami. Jednakże spotkałem się z nieskomplikowanymi rozwiązaniami IT, dobrze funkcjonującymi i już zintegrowanymi bez szyny, gdzie postanowiono ją wprowadzić z powodów klasy: „tak będzie lepiej”, „to standard w dużych firmach, pokażemy, że też jesteśmy duzi”. Nowy element niewiele tam wniósł. Nie są wykorzystywane żadne odwzorowania, transformaty danych.

Nie neguję stosowania sprawdzonych rozwiązań. A nawet dodam, gorąco zachęcam do tego. Artykułem tym chciałbym jedynie podnieść tendencję do częstszego wysławiania pytania: po co? oraz odnajdywania na niego odpowiedzi opartej nie tylko na haśle, ale krytycznym osadzeniu wzorca w spodziewanej rzeczywistości.

Krzysztof Wilniewczyc jest Solution Architektem w Asseco Poland.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *