API, Use Case i Cyfrowa Transformacja

Wprowadzenie

W kwietniu 2024 roku opisywałem API

Artykuł powyższy polecam osobom zainteresowanym stroną techniczną projektowania integracji i API. Dzisiaj odpowiem na problemy jakie zgłaszają prawnicy, czyli co jest przedmiotem umowy gdy przedmiotem tym jest mityczne API. (https://jaroslawzelinski.co.uk/2024/04/05/api-to-cos-innego-nie/

Wtedy problemem była próba traktowania oryginalnego API systemu ERP jak dedykowanej aplikacji, czy odrębnego kodu (licencja na API). Problemem jest często także poprawne nazewnictwo tego “o czym mowa”, są to nieporozumienia (lub celowe działanie) wywoływane ukrywaniem architektury przez dostawcę oprogramowania. W między czasie trafiła się dzisiaj ciekawa, krótka dyskusja o API na “Anonimowy Czat“.

Dzisiaj na konferencji Kongres Cyfrowa Transformacja, miałem referat zatytułowany “Różne podejście do transformacji cyfrowej w obliczu długu technologicznego i systemów “legacy”” (które czasami nazywamy dinozaurem w serwerowni). Na konferencji opisywałem “cyfrową transformację” z systemem “legacy” i polecałem metodę, którą nazwałem “Opakowanie i powolna migracja”.

Opakowanie i powolna migracja

Jak są definiowane przestarzałe aplikacje, zwane z ang. “legacy systems”:

  • Przestarzały system (ang. legacy system) to przestarzałe oprogramowanie i/lub sprzęt komputerowy, który jest nadal w użyciu. System taki nadal spełnia potrzeby, dla których został pierwotnie zaprojektowany, ale nie pozwala na rozwój. Taki system z reguły spełnia aktualne wymagania ale żadne inne, nie pozwoli także na współdziałanie z nowszymi systemami.
  • Wraz z postępem technologicznym, wiele firm ma do czynienia z problemami spowodowanymi przez istniejące przestarzałe systemy. Mówi się, że system taki zamiast oferować firmom najnowsze możliwości i usługi – takie jak np. cloud computing, integracja z otoczeniem – utrzymuje firmę w biznesowej “skamielinie”, blokując rozwój lub bardzo podnosząc koszty tego rozwoju.

Co możemy zrobić? Do wyboru mamy:

  • Wymiana na nowy
  • Modyfikacja
  • Opakowanie i powolna migracja

Wymiana na nowy to mała rewolucja. Modyfikacja jest już niemożliwa (patrz wyżej). Skutecznym sposobem “wyjścia” z przestarzałej aplikacji jest jej “opakowanie”, wybór nowej nowoczesnej i powolna (bez-rewolucyjna) migracja. Dlaczego akurat tak?

  • Stare systemy mają bardzo często proste API zbudowane na poleceniach CRUD lub “wystawiają bazé danych” (dostęp z pomocą ODBC/SQL).
  • Nowe systemy z reguły mają nowoczesne API realizujące złożone operacje.

Na czym polega “opakowanie i migracja”?

  • Dla starego sytemu budujemy pośredniczący adapter (opakowanie) realizujący “nowoczesne API”.
  • Iteracyjnie “przepinami” operacje ze starego systemu na nowy.

Schematycznie wygląda to tak:

Dobre i złe API

Kontynuując ten wątek postanowiłem najpierw opisać problem z API stanowiący ekspozycje tabel baz danych lub tak zwanych CRUD-ów (cztery operacje ang. Create, Retrieve, Update, Delete, czyli Utwórz, Przywołaj, Aktualizuj, Usuń) oraz budowę fasady, inteligentnego adaptera oferującego “inteligencje API”.

Praktyki projektowania znane z tak zwanych anemicznych modeli dziedziny (stare aplikacje odtwarzające w kodzie struktury relacyjnych baz danych: tabele i kolumny) są od wielu lat uznawane za antywzorce projektowe (Anemic domain model):

Ten wzorzec jest powszechnym podejściem w aplikacjach JavaEE, prawdopodobnie wspieranym przez technologie takie jak wczesne wersje Entity Beans EJB, a także w aplikacjach .NET zgodnych z architekturą Three-Layered Services Application, gdzie takie obiekty należą do kategorii “Business Entities” (chociaż Business Entities mogą również zawierać zachowanie).

Popatrzmy na popularny w sieci referat: Can Great Programmers Be Taught?, którego autorem jest John Ousterhout. Nas interesuje tu idea szerokości (liczba operacji) i głębokości (logika) interfejsu: dobry interfejs “hermetyzuje” maksymalnie wiele: jest wąski i głęboki. Dlaczego akurat taki jest lepszy? Bo mała liczba operacji interfejsu obiektu powoduje, że nie powstają silne i złożone zależności między tym obiektem a pozostałymi, korzystającymi z jego usług oraz ukrywa on w sobie (hermetyzuje) logikę korzystania z danych jakimi zarządza. Skorzystanie z takiego API jest łatwe i nie wymaga wiedzy dziedzinowego systemu z którego chcemy skorzystać:

Ludzie programują komputery od ponad 80 lat, ale nie ma zgody co do tego, jak projektować oprogramowanie, a nawet jak wygląda dobry projekt. Jako społeczność mówimy dużo o narzędziach i procesach, ale prawie wcale o projektowaniu. W tym wystąpieniu opiszę moją ostatnią pracę nad identyfikacją i komunikacją zestawu zasad projektowania oprogramowania, w tym nowy kurs projektowania oprogramowania w Stanford, który jest prowadzony bardziej jak seminarium z pisania w języku angielskim niż tradycyjne zajęcia z programowania, oraz książkę o projektowaniu oprogramowania, która opiera się na koncepcjach z zajęć. Przedstawię również kilka zasad projektowania, takich jak “klasy powinny być głębokie” i “definiowanie błędów poza nimi”.

Dlatego szczytem nieefektywności są tabele baz danych odwzorowywane jako klasy, ich pola jako atrybuty, a pobieranie i zapisywanie danych to kaskady wywołań operacji get/set dla każdego atrybutu (patrz: Why getters and setters are terrible), (wzorzec Active Table lub Active Record, patrz także: ORM anti-pattern series) . Nie ma tu żadnej logiki, ta wymaga implementacji po stronie pobierającego dane (skrypty transakcyjne), w osobnych klasach grupowanych często w drzewa dziedziczenia i kompozycji (patrz art. Dziedziczenie – anatomia trzydziestopięcioletniego błędu).

API czyli co?

API (Application Programming Interface), najczęściej jest definiowane jako:

zestaw reguł, protokołów i narzędzi, które umożliwiają różnym aplikacjom komunikację i wymianę danych między sobą. Interfejsy API działają jako pośrednik, definiując sposób, w jaki jeden program może zażądać usługi lub informacji od innego programu, bez konieczności znajomości wewnętrznego kodu drugiego. Na przykład, aplikacja pogodowa używa API do pobierania danych z serwera usługi pogodowej, który przetwarza żądanie i wysyła odpowiedź z powrotem do aplikacji.

Kluczowe jest tu stwierdzenie: “jeden program może zażądać usługi lub informacji od innego programu, bez konieczności znajomości wewnętrznego kodu drugiego“. Kluczowe cechy:

  • Model klient-serwer: Aplikacja wysyłająca żądanie jest “klientem”, a aplikacja dostarczająca informacje lub usługę jest “serwerem”.
  • Żądanie i odpowiedź: Klient wysyła żądanie do serwera, serwer przetwarza żądanie i wysyła odpowiedź klientowi.
  • Standardowy format: Komunikacja odbywa się przy użyciu standardowego formatu, takiego jak JSON lub XML.

Generalnie “API serwera” to zestaw udostępnionych i udokumentowanych publicznych poleceń realizowanych przez ten serwer. Na poziomie projektowania mówimy, że API to operacje publiczne klasy (komponentu).

Płytkie szerokie vs Głębokie wąskie API

Popatrzmy na ten schemat z ww. prezentacji:

Zielona linia reprezentuje liczbę operacji, niebieskie pole to funkcjonalność oferowana przez tę klasę [zotpressInText item=”{5085975:Q3D38MIU}”].

Nawiązując do ww. referatu i koncepcji profesora (John Ousterhout):

  • liczba operacji kasy (jej interfejs) to jego “szerokość”,
  • “zwężenie” interfejsu wymaga rozbudowania procedur czyli metod klasy, to jest “pogłębienie klasy”,
  • wąski interfejs hermetyzuje logikę np. logikę biznesową,
  • wąskie klasy podnoszą bezpieczeństwo korzystania z serwera i zmniejszają uzależnienie kodu aktorów.

Popatrzmy na ten diagram:

Korzystanie z szerokiego płytkiego API (diagram klas UML)

Mamy tu Aktora (aplikacja kliencka) oraz API Serwera (jakaś aplikacja, system ERP, sklep internetowy itp.). Pokazane tu API oferuje szereg prostych operacji (z reguły są to zestawy poleceń CRUD do wielu tabel lub atrybutów dokumentów). Aktor korzystający z takiego API musi posiadać wiedzę po co i w jakiej kolejności te operacje wywołać by uzyskać oczekiwany efekt, czyli po stronie aktora realizowana będzie procedura:

Metoda operacji (usługi), diagram aktywności UML.

Problem w tym, że:

  • aktor musi mieć wiedzę o tym “jak to zrobić” (ww. procedura).
  • mając ograniczoną wiedzę o serwerze może to zrobić źle,
  • zmiana API Serwera wymaga refactoringu wszystkich tych procedur.

Jak inaczej? Przede wszystkim budujemy adapter, który separuje (kod) Aktora od API Serwera:

Separacja API Serwera i Aktora z pomocą Adaptera (diagram klas UML)

Pokazana wcześniej procedura (metoda) będzie ukryta (hermetyzacja) w tym Adapterze i widoczna jako jedna usługa (operacja) Adaptera (patrz także Anti-Corruption Layer (ACL)).

API jako model przypadków użycia UML

API (podobnie jak GUI) to przypadki użycia systemu. Jak to określa specyfikacja UML:

Przypadki Użycia są sposobem na określenie wymagań wobec systemów, tj. tego, co systemy mają robić [a nie jak]. Kluczowe pojęcia to Aktor, Przypadek Użycia i “przedmiot” specyfikowania. Każdy “Przypadek Użycia przedmiotu analizy” reprezentuje zachowanie systemu którego dotyczy. Użytkownicy i wszelkie inne systemy, które mogą wchodzić w interakcje z podmiotem, są reprezentowane jako jako Aktorzy. […] Przypadek Użycia jest rodzajem klasyfikatorem, który reprezentuje deklarację zestawu oferowanych zachowań. Każdy Przypadek Użycia to określone zachowanie, które system może wykonać na żądanie jednego lub większej liczby aktorów. Przypadek Użycia definiuje zachowanie systemu bez odniesienia do jego wewnętrznej struktury. [zotpressInText item=”{5085975:DCYU6XZJ}”]

Projektując zmiany w systemie często specyfikujemy wymagania w postaci przypadków użycia. Dotyczy to także obszaru integracji, wtedy przypadek użycia to operacja na API:

Abstrakcja Systemu w postaci diagramu przypadków użycia (UML)

Taki diagram wydaje się nie mieć sensu i często jest to prawdą. Jednak możemy zażądać by aplikacja integrowana miała wąski i głęboki interfejs (API) i pokazać to tak:

Wtedy pokazana wcześniej metoda będzie tu scenariuszem przypadku użycia.

Podsumowanie

A teraz wyobraźmy sobie, że mamy dinozaura, starą aplikację, której specyfikacja API wygląda jak ankieta u dentysty (powyższe API Serwera). Jest bardzo prawdopodobne, że te polecenia to pojedyncze linie kodu lub zapytania SQL do bazy danych RDBMS tej aplikacji. Integracja z resztą systemu IT organizacji będzie trudna i kosztowna w wykonaniu i utrzymaniu. Przepięcie ten aplikacji na nową też nie będzie łatwe (co na co przepinać?).

Jak inaczej? Żądamy od dostawcy (lub lokalnego administratora) napisania Adaptera. Procedury dla Adaptera (metody operacji) albo napisze nasz admin (być może twórca) starej aplikacji, albo dostawca aplikacji (generalnie warto żądać takiego Adaptera już na etapie specyfikowania wymagań na etapie zakupu, albo zlecić jego zbudowanie po wdrożeniu). W przypadku planowania migracji do nowej aplikacji musimy zdecydować kto taki adapter zaprojektuje i wykona wdrożenie. Niewątpliwie pozostawienie Serwera w stanie “as-is” będzie bardzo kosztownym w utrzymaniu dziedzictwem [zotpressInText item=”{5085975:DQ85BDLN}”].

A od strony prawnej? Samo “wystawianie” API Serwera nie jest żadnym dodatkowym przedmiotem prawa autorskiego, ale może być objęte dodatkową licencją (np. w przypadku SAP). Adapter jako osobna dedykowana aplikacja to nic innego jak utwór, dedykowany produkt, i jak najbardziej chroni go prawo autorskie, by moze także jest chroniony jako know-how. Pozostaje zdecydowanie o tym komu zostanie zlecone jego zaprojektowanie.

Źródła

[zotpressInTextBib style=”apa” sort=”ASC”]

Wprowadzenie

Aplikacje Jira i Confluence firmy Atlassian są bardzo popularne nie tylko w działach IT. Obie wspierają między innymi prace analityczne i projektowe na poziomie zadań (Jira) i treści (Confluence).

Producent pakietu CASE Visual Paradigm, od wersji 17.3 wspiera integrację swojego pakietu z tymi aplikacjami, w efekcie w tych środowiskach możliwa stała się pełna integracja zespołu projektowego wykonawczego z analitykami i projektantami.

Płynna integracja z Atlassian Confluence

Zastosowanie:

  • Agile Development: Udostępnianie zaktualizowanych przepływów pracy i diagramów UML w dokumentacji sprintu.
  • Zarządzanie produktem: Osadzanie modeli procesów na stronach specyfikacji produktu.
  • Architektura korporacyjna: Centralizacja wizualizacji architektury w Confluence dla “jednego źródła prawdy”.
  • Szkolenia i wsparcie: Zapewnienie przejrzystych pomocy wizualnych w dokumentacji wdrożeniowej i dokumentacji wsparcia.
  • Zarządzanie projektami: Bez wysiłku ręcznej synchronizuj kamienie milowe, diagramy ryzyka i plany projektów.

Co musi zrobić użytkownik Confluence: Atlassian Confluence Integration.

Płynna integracja z Atlassian Jira

Wprowadzenie

Czym jest dziedziczenie?

Dziedziczenie umożliwia tworzenie nowych klas, które ponownie wykorzystują, rozszerzają i modyfikują zachowanie zdefiniowane w innych klasach. Klasa, której elementy są dziedziczone, nazywana jest klasą bazową, a klasa, która dziedziczy te elementy, nazywana jest klasą pochodną. Klasa pochodna może mieć tylko jedną bezpośrednią klasę bazową.

Konstrukcja ta wywodzi sie z generalizacji: jej założeniem było usuwanie z kodu domniemanych redundancji na wzór pojęciowego związku generalizacji. Kluczowy problem polega na tym, że takie podejście czyni z każdej aplikacji monolit o bardzo złożonej strukturze kodu źródłowego. Problem w tym, że formalnie dziedziczenie nigdy nie było cechą (istotą) obiektowego paradygmatu programowania, a jednak mimo to producenci “obiektowo-zorientowanych” języków programowania podkreślali tę cechę swoich narzędzi, podchwyciły to także uczelnie i w programach nauczania i podręczników, pod pojęciem paradygmatu obiektowego, promowano takie cechy jak “dziedziczenie i łączenie danych i funkcji w obiekty”, które de facto obie są antywzorcami projektowania.

W efekcie powstały ogromne ilości kodu bardzo trudnego w utrzymaniu i rozwoju co zaczęło powodować narastająca dyskusję na temat tego problemu. Artykuł zainspirowany referatem umieszczonym na końcu tego tekstu.

Krótka historia problemu

1965 – Simula, pierwszy obiektowy język programowania.

1966 – Alan Kay publikuje ideę Obiektowego Paradygmatu Programowania, nie jest to dziedziczenie i łączenie funkcji i danych w obiekty, Paradygmat Obiektowy to hermetyczne (zamknięte) obiekty i wymiana komunikatów między nimi, kluczową cechą obiektów jest hermetyzacja i polimorfizm (odpowiedź na żądanie zależy od tego do jakiego obiektu zostało ono wysłane, a nie od tego jak ono brzmi).

1972 – powstaje obiektowo zorientowany język Smalltalk, nie wspiera on dziedziczenia.

1985 – powstaje C++, wersja C pozwalającą na pisanie w duchu OOP+dziedziczenie.

1995 – “Kompozycja zamiast dziedziczenia: Faworyzuj kompozycję obiektów zamiast dziedziczenia klas”. [Gamma, Erich, ed. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional Computing Series. Addison-Wesley, 1995.]

1998 – narasta problem z systemami ERP i FinTech, bo kończy sie era mainframe a zaczyna PC, a aplikacje zbudowane są często na modelach relacyjnych danych (model relacyjny powstał w 1972), pełna, bezstratna migracja z baz relacyjnych do innych formatów w zasadzie jest niemożliwa, więc powstał pomysł by te tabele relacyjne odwzorować w kodzie (mapowanie ORM, dziedziczenie, kompozycje): powstała JavaEE (Oracle) i słynny “anemiczny model dziedziny”, który już po niecałych 10 latach uznano za antywzorzec projektowy.

1998 – Alan Kay, pomysłodawca “obiektowości”, dostrzega problem i przypomina: “Paradygmat obiektowy to nie dziedziczenie i łączenie funkcji i danych w obiekty! To hermetyzacja obiektów i wymiana komunikatów między obiektami!”

1998Profesor Ousterhout ( Stanford University) pisze: “Innym problemem związanym z językami obiektowymi jest ich nacisk na dziedziczenie. Dziedziczenie implementacji, w którym jedna klasa pożycza kod napisany dla innej klasy, jest złym pomysłem, który utrudnia zarządzanie oprogramowaniem i jego ponowne wykorzystanie. Wiąże ono ze sobą implementacje klas w taki sposób, że żadna z nich nie może być zrozumiana bez drugiej: podklasa nie może być zrozumiana bez wiedzy o tym, jak odziedziczone metody są zaimplementowane w jej nadklasie, a nadklasa nie może być zrozumiana bez wiedzy o tym, jak jej metody są dziedziczone w podklasach. W złożonej hierarchii klas, żadna pojedyncza klasa nie może być zrozumiana bez zrozumienia wszystkich innych klas w hierarchii. Co gorsza, klasa nie może być oddzielona od swojej hierarchii w celu ponownego użycia. Dziedziczenie wielokrotne jeszcze bardziej pogarsza te problemy. Dziedziczenie implementacji powoduje to samo przeplatanie i kruchość, które zaobserwowano w przypadku nadużywania instrukcji goto. W rezultacie systemy zorientowane obiektowo często cierpią z powodu złożoności i braku możliwości ponownego wykorzystania.” [Ousterhout, J.K. ‘Scripting: Higher Level Programming for the 21st Century’. Computer 31, no. 3 (1998): 23–30. https://doi.org/10.1109/2.660187].

2002 – Microsoft konkurując z Oracle (EJB/JavaEE) wypuszcza oficjalną wersję .NET Framework z językiem C# (zbudowany na wzór C++), podobnie jak Java wykorzystuje dziedziczenie i ORM.

2003 – A. Cockburn (sygnatariusz Agile Manifesto) wskazuje, ze wzorce projektowe znane z C++/JavaEE są dobre dla środowiska (elementy systemu) ale szkodliwe dla aplikacji biznesowych, publikuje “Architekturę Heksagonalną” (separacja kodu logiki biznesowej aplikacji i kodu środowiska aplikacji)

2007 – jedna z bardziej znanych publikacji opisująca szkodliwość odwzorowywania modeli pojęciowych bezpośrednio w kodzie z pomocą dziedziczenia.

2014 – Fowler (sygnatariusz Agile Manifesto) oficjalnie krytykuje dziedziczenie i anemiczny model dziedziny (klasy odwzorowujące pojęcia i ich związki) jako antywzorzec projektowy.

2015 – OMG usuwa związek dziedziczenia z UML (zostaje jedynie generalizacja).

2015 – W odpowiedzi pojawia się jeden z bardziej znanych referatów: Bjarne Stroustrup – Object Oriented Programming without Inheritance – ECOOP 2015.



2018 – R.C. Martin wraz Simonem Brownem (Arch. C4) publikują “Clean Architecture”, opisują swoje pozytywne doświadczenia z podziałem na komponenty, separacją aplikacji od jej środowiska i używaniem UML na etapie projektowania poprzedzającego kodowanie. Całkowicie ignorują dziedziczenie jako element architektury (patrz także ciekawy, jeden z wielu podobnych, artykuł z tego samego roku na ten temat: Inheritance is Indeed Evil).

2020 – dość głośny artykuł w Medium zatytułowany “Inheritance is Indeed Evil” [Kock, Martin. ‘Inheritance Is Indeed Evil’. Medium, 19 January 2020. https://medium.com/@cookie80/inheritance-is-indeed-evil-ee759b87ad4d.]

2020 – referat Anjana Vakil, która opisuje jak wyciąga projekty z kłopotów: jest to całkowita rezygnacja z języków C++ i JavaEE i rezygnacja z dziedziczenia w kodzie:

Ten wykład to historyczna i filozoficzna podróż w głąb serca ciemności, czyli programowania obiektowego (OOP). Dołącz do mnie, gdy będę wstrząśnięty odkryciem, że obiekty i klasy nie są najważniejszymi koncepcjami OOP: są nimi wiadomości i późne wiązanie. Spróbujemy zajrzeć do głów Alana Kaya i innych twórców OOP, gdy tworzyli języki takie jak Smalltalk, i odkryjemy, że te “stare” pomysły wydają się dziś uderzająco aktualne. Nasze szczęki mogą opaść, gdy zdamy sobie sprawę, że OOP i programowanie funkcyjne nie różnią się tak bardzo, jak nam się wydawało, a pierwszy język OO nie powstał w latach 60-tych czy 70-tych, ale dużo, dużo wcześniej… Co czeka nas na końcu tej podróży? W najgorszym przypadku przejdziemy krótki kryzys wiary we wszystko, co kiedykolwiek myśleliśmy, że wiemy o programowaniu. (W najlepszym przypadku zmienimy sposób, w jaki postrzegamy ten niemal powszechny, ale często niezrozumiany paradygmat i odejdziemy z nowymi spostrzeżeniami na temat tego, jak projektujemy i rozumiemy nasz kod.

2021 – referat Dave’a Farley’a “Object Oriented Programming vs Functional Programming”, w którym wskazuje, że paradygmat obiektowy to nie dziedziczenie:

Programowanie obiektowe było dominującym podejściem przez ostatnie kilka dekad, ale języki programowania funkcjonalnego zyskują coraz większą popularność i wpływy. Czy zatem programowanie obiektowe jest przestarzałe? Czy programowanie funkcyjne to moda, która po prostu mija się z celem?

Wielu programistów funkcyjnych uważa, że programowanie obiektowe jest złe. Wielu programistów OO uważa, że programowanie funkcyjne nie jest skalowalne, jaka jest prawda?

W tym odcinku Dave Farley analizuje kwestię inżynierii oprogramowania dotyczącą programowania obiektowego i funkcjonalnego. Skąd wzięły się te idee, co oznaczają i dlaczego mają znaczenie. Ponadto, czy istnieją inne pomysły, które mogą pojawić się w przyszłości?

2025 – czerwiec 17, głośny referat Casey”a Muratori: “The Big OOPs: Anatomy of a Thirty-five-year Mistake” – BSC 2025 (Better Software Conference):

Moja krótka historia

1985 – moje pierwsze programy w Fortranie (tu mnie nauczono rysowania przed kodowaniem, pomaga do dzisiaj).

1989 – Basic, Pascal, C, C++, pierwsze przygody z kodowaniem i nauczaniem programowania studentów. Uznałem, że dziedziczenie to zmora, od tej pory nie korzystałem z tej konstrukcji.

1991 – początek przygody z system UNIX, projektowanie dedykowanych podsystemów dla dużych firm i urzędów, głównie Perl.

1995 – po przygodach z ORM i dziedziczeniem w Oracle (nazywali to obiektowe bazy danych) odpuściłem sobie całkowicie SQL i relacyjne modele danych, wiele moich aplikacji zaczęło powstawać na systemie plików w UNIX (kilkaset tysięcy rekordów bywało szybsze niż tabele Informix), później przeszedłem na SGML, potem na XML, motory SQL zacząłem wykorzystywać tylko jak pojedyncze tabele, tak jak późniejsze bazy key-value i bazy dokumentowe (NoSQL).

1998 – pierwsze profesjonalne przygody z UML w projekcie dla banku (system scoringowy dla Kredyt Bank SA)

2004 – rozpoczęcie pracy w roli samodzielnego kontraktowego analityka-projektanta aplikacji (nadal).

2005 – dostałem zaproszenie do prowadzenia wykładów i laboratoriów dla studentów Akademii Morskiej (obecnie Uniwersytet Morski) w Gdyni, później Akademia WIT w Warszawie przy Polskiej Akademii Nauk.

Wprowadzenie

Często spotykaną definicją mikro-serwisów jest

W inżynierii oprogramowania architektura mikro-serwisów to wzorzec architektoniczny, który organizuje aplikację w zbiór luźno powiązanych, drobnoziarnistych usług, które komunikują się za pośrednictwem lekkich protokołów. Wzorzec ten charakteryzuje się możliwością niezależnego opracowywania i wdrażania usług, poprawiając modułowość, skalowalność i zdolność adaptacji. Wprowadza on jednak dodatkową złożoność, szczególnie w zarządzaniu systemami rozproszonymi i komunikacją między usługami, czyniąc początkową implementację trudniejszą w porównaniu do architektury monolitycznej. (https://en.wikipedia.org/wiki/Microservices)

To samo źródło dalej:

Nie istnieje jedna, powszechnie uzgodniona definicja mikro-serwisów. Ogólnie jednak charakteryzują się one skupieniem na modułowości, a każda usługa jest zaprojektowana wokół konkretnych możliwości biznesowych. Usługi te są luźno powiązane, niezależnie wdrażane i często opracowywane i skalowane oddzielnie, co zapewnia większą elastyczność i zwinność w zarządzaniu złożonymi systemami. Architektura mikro-serwisów jest ściśle powiązana z zasadami takimi jak projektowanie oparte na domenie, decentralizacja danych i zarządzanie oraz elastyczność wykorzystania różnych technologii dla poszczególnych usług, aby jak najlepiej spełnić ich wymagania.

Czytamy “zbiór luźno powiązanych, drobnoziarnistych usług” co niestety “Wprowadza […] jednak dodatkową złożoność, szczególnie w zarządzaniu systemami rozproszonymi i komunikacją między usługami, czyniąc początkową implementację trudniejszą w porównaniu do architektury monolitycznej.” Idąc takim tropem często niestety otrzymujemy to:

Więc jak uniknąć “Gwiazdy Śmieci”?

Architektura

Problem Gwiazdy Śmierci usuwamy rezygnując z “drobnoziarnistych usług, które komunikują się“. Mikro-serwisy to jednak niewątpliwie komponenty (modułowość) czyli niezależne (hermetyzacja) i dziedzinowe (zakres odpowiedzialności) jednostki. Po drugie wymiana danych między komponentami jest jednak czasami konieczna, więc czy to jest sprzeczność? Nie.

Micro serwisy są często pokazywane jako architektura poniżej:

(źr.: https://www.redhat.com/en/topics/microservices/what-are-microservices)

Nie ma żadnej komunikacji między mikro-serwisami ale jest UI. Wielu autorów przedstawia taki oto przykład:

Chris Richardson. (2015, June 15). Building Microservices: Using an API Gateway [Company Blog]. F5, Inc.https://www.f5.com/company/blog/nginx/building-microservices-using-an-api-gateway

Ale przecież np. Zamówienie powstaje na podstawie Koszyka. To jak? Zamówienie ma sobie brać coś bazy danych koszyka? Nie tak powstają monolity i Gwiazdy Śmierci! Czym więc realnie jest API Gateway? Wygląda na to, że jest także szyną integracyjną, która zawiera scenariusze każdej usługi (Przypadek Użycia), np. Zamówienia (na podstawie Koszyka i należnego Upustu), jednak powyższy diagram tego nie tłumaczy. Potrzebny jest pełniejszy projekt.

Projektowanie

Popatrzy na kolejny przykład, który bardzo często pojawia się u autorów w Internecie. Naliczyłem kilkanaście stron w Internecie z tym oto schematem:

Aby opracować projekt z narzędziem CASE odwzorujmy to w UML. Najpierw spróbujmy odtworzyć wymagania w postaci przypadków użycia:

Kolejny krok to wstępna wizja pracy z tym sklepem:

Projektujemy architekturę HLD, która zapewni realizację ww. wymagań (funkcjonalności):

Tu kilka wyjaśnień. Należy pokazać źródło danych takich jak np. aktualna data: API_Environmant (wzorzec architektura heksagonalna). Separujemy otoczenie od naszego kodu adapterami (hermetyzacja ERP i e-Płatności). Komponenty się wzajemnie nie widzą, więc nie zależ od siebie.

Scenariusz realizacji zamówienia:

Powyższe to umowa z zamawiającym, ale to także test odbiorczy (UAT, przypadki użycia, ich specyfikacje, to testy odbiorcze).

Kolejny etap to ostateczny test architektury i projekt implementacji:

Operacja wstawienia treści Koszyka do Zamówienia wymaga zdefiniowania koszyka:

Oraz Zamówienia:

Procedurę wstawienia do zamówienia daty oraz treści koszyka można opisać słownie lub z pomocą diagramu aktywności (to opisałem w innym miejscu: Diagram aktywności UML). Metodę projektowania wraz z bogate literaturą czytelnik z znajdzie w tekście: ICONIX jako zwinny proces projektowania oprogramowania z użyciem UML.

Podsumowanie

Programming is not solely about constructing software—programming is about designing software. [Ipek Ozkaya. ‘Building Blocks of Software Design’. IEEE Software 37, no. 2 (2020): 3–5. https://doi.org/10.1109/MS.2019.2959049.]

Pokazano standardowy sposób tworzenia modeli PIM (Platform Independent Model, OMG.org). Mikro-serwisy to bardzo wygodna, od wielu lat opisywana architektura, pierwotnie nie była tak nazywana, ponad 20 laty temu można znaleźć opis tej idei jako wzorzec SAGA i komponentowa architektura aplikacji. Era “drobienia” jest nowa i szybko się skompromitowała powstaniem Gwiazd Śmierci, i świat wraca powoli do źródeł tej koncepcji.

Wprowadzenie

W marcu 2021 w artykule Struktury formularzy jako forma wyrażania wymagań skupiłem się na detalach treści formularzy ekranowych, jako elementach specyfikowania przypadków użycia. Artykuł ten sprowokował pytania o związki między formularzami, ich przepływy oraz niekończące się pytania o cel stosowania związków extend i include używane często niepoprawnie w celu pokazania “przepływu ekranów”. Dlatego napisałem post będący rodzajem kontynuacji tego tekstu.

W toku analizy biznesowej, a potem projektowania oprogramowania, powstają różne produkty: modele procesów biznesowych, modele architektury systemów, scenariusze integracji, definicje interfejsów, i inne. Większość ich jest niezrozumiała dla przeciętnego uczestnika takiego projektu, który generalnie czeka na oprogramowanie zaspokajające jego potrzeby. Jak z nimi rozmawiać?

Proces biznesowy zobrazowany jako schemat blokowy na ustalonym poziomie szczegółowości, jest zrozumiały dla większości, po ewentualnym krótkim przeszkoleniu. Jednak czytanie modeli wnętrza systemów informatycznych, wykonanych z użyciem UML, niestety już nie jest proste i wymaga znacznie większych kompetencji. Tu już adresatem jest przyszły wykonawca (developer). Co więc pokazać Zamawiającemu? Makietę!

Interfejs użytkownika to jedyne co widzi użytkownik, dlatego bardzo często jest to – makiety interfejsu użytkownika – podstawowa metoda komunikowania przyszłemu użytkownikowi (zamawiającemu analizę i projekt) efektów pracy projektanta oraz sposób na konsultowanie tego z Zamawiającym. To co jest “pod maską” samochodu to domena projektanta, który musi zapewnić realizację (mechanizm działania) tego co powinien zobaczyć kierowca na tablicy rozdzielczej. Jednak nie oczekujemy, że przeciętny użytkownik samochodu ma kompetencje konstruktora samochodów i ze zrozumieniem czyta jego dokumentację techniczną, albo co gorsza, zaproponuje jakieś “dobre rozwiązanie”.

Opis

Praca analityka projektanta to obietnica, że zaprojektowany mechanizm, realizujący to co pokazują makiety ekranów, jest poprawny (zadziała). Weryfikatorem tej obietnicy jest etap implementacji.

Wizja czyli LOFI

Makiety LOFI (ang.: Low Fidelity, niska jakość graficzna) robimy na wczesnym etapie projektowania. Ich prostota i ogólność pozwala skupić się na projekcie i koncepcjach. Na tym etapie poświęcamy czas na ideę systemu. Skupiając się na istocie problemu możemy, niemalże w czasie rzeczywistym, zbierać opinie zamawiającego na temat szkicowanego prototypu. Na podstawie tych opinii można szybko, minimalnym nakładem pracy, przerobić makietę w ciągu zaledwie kilku minut.

Często początkiem jest wizja scenariusza pracy z aplikacją w określonym kontekście:

Przykład scenariusza i makiet LOFI

Kolejny etap to uzgodnienie treści każdego ekranu ale nadal na poziomie LOFI:

Koszyk Produktów

Idziemy do celu

Makieta HIFI (ang. High Fidelity, makieta wysokiej jakości) to generalnie projekt na etapie implementacji, ewentualnie może być potrzebny w końcowym etapie projektowania, szczególnie gdy rozwijamy istniejącą aplikację i mamy do dyspozycji “prawdziwe ekrany”. Wtedy możemy tworzyć makiety HIFI na bazie zrzutów ekranów istniejącej aplikacji i edytować je na poziomie HIFI:

(reedycja HIFI na bazie zrzutu istniejącego ekranu)

Kluczowym celem prototypowania HIFI jest uzgodnienie ostatecznej wersji ekranów, zanim zacznie się ich implementacja (ostateczną wersję i tak przedstawi developer).

Cały czas pamiętamy, że kodowanie jest najdroższym elementem projektu i należy go minimalizować na rzecz projektowania.

Prezentacja makiet HIFI pozwala też na uzyskanie ostatecznych szczegółowych informacji zwrotnych na temat określonych elementów projektu, co nie byłoby możliwe przy użyciu makiet LOFI. Jeżeli mamy możliwość pokazania animacji, to klient ma możliwość zapoznania sie z tym co dostanie i zgłosić swoje uwagi dot. np. ergonomii.

Korzyści z prototypowania GUI

Opracowywanie makiet to tak zwany “rapid prototyping”, czyli szybkie prototypowanie. W fazie projektowania, znacznie efektywniejsze niż dostarczanie kolejnych działających wersji aplikacji na etapie implementacji. To większa możliwość prezentacji efektów dla interesariuszy: klienci otrzymają jasne wyobrażenie o tym, jak produkt będzie wyglądał i działał, zanim jeszcze zacznie powstawać. Na tym etapie można też ustalić jasne oczekiwania z deweloperami na wczesnych etapach ich pracy, w tym ile czasu będzie potrzebne do zbudowania prototypu i posiadania gotowego produktu.

Korzyści z narzędzi wbudowanych do CASE

Sam prototyp to tylko połowa sukcesu. W końcu te ekrany są konsekwencją (lub początkiem) projektu architektury wykonanego z jakimś narzędziu CASE z użyciem notacji UML. Dlatego bardzo ważne jest połączenie treści tych makiet ze słownikami, modelami danych oraz oczywiście z przypadkami użycia i ich scenariuszami.

Dzięki temu jesteśmy w stanie utrzymać spójność całego projektu, a także wygenerować pełną dokumentację, bez potrzeby zbierania efektów pracy z kilku różnych aplikacji, co niestety bywa ogromną pracą, także przy każdej aktualizacji.

Podsumowanie

Praca z prototypami ekranów aplikacji (wireframes) to nie tylko ich treść ale przede wszystkim ergonomia i efektywność, a ideałem jest prototypowanie w czasie rzeczywistym, czyli praktycznie równolegle ze słownym opisywaniem potrzeb w rozmowie. To co prawda najrzadsza forma pracy, ale na etapie szybkiego szkicowania scenariuszy nie taka rzadka.

Warto wiedzieć, że w praktyce narzędzia do prototypowania doskonale się sprawdzają w roli szybkiego tworzenia/dokumentowania formularzy w modelach procesów biznesowych (obiekty DataObject w notacji BPMN). Zamiast przygotowywać szablony dokumentów z pomocą standardowych edytorów tekstów, arkuszy kalkulacyjnych czy narzędzi do przygotowywania prezentacji, znacznie efektywniejsze jest tworzenie w narzędziu CASE makiet tych dokumentów i logiczne ich łączenie z elementami na modelach BPMN i UML.

Prezentacja

W tym momencie przerwę pisanie a zaprezentuję narzędzie którego od lat sam używam.

Prototypowanie przepływy makiet.
Prototypowanie LOFI poszczególnych makiet oraz zmiany stanów ekranów.
Praca z Visual Paradigm na makietach LOFI
Łączenie makiet LOFI z elementami scenariuszy przypadków użycia.

Więcej na stronie: UX Design and Wireframe Tools