1/50
Definicja usług sieciowych
Usługa sieciowa to fundamentalny komponent współczesnej informatyki, umożliwiający komunikację i wymianę danych między różnymi aplikacjami lub systemami za pośrednictwem sieci komputerowej.
  • W praktyce jest to oprogramowanie, które wykonuje określone zadania, takie jak dostarczanie danych, uwierzytelnianie użytkowników czy przetwarzanie transakcji, udostępniając swoje funkcje innym programom.
  • Usługi te działają w oparciu o standardowe protokoły, na przykład HTTP, co zapewnia interoperacyjność, czyli zdolność do współpracy systemów stworzonych w różnych technologiach.
  • Dzięki temu deweloperzy mogą budować złożone aplikacje, składając je z mniejszych, wyspecjalizowanych usług, co znacząco przyspiesza rozwój i ułatwia utrzymanie systemów.
Ilustracja dla slajdu 1

Usługa sieciowa to nie tylko program dany użytkownikom, ale przede wszystkim zbiór ściśle zdefiniowanych reguł i protokołów komunikacyjnych, które umożliwiają wymianę informacji. W praktyce każde nowoczesne przedsiębiorstwo opiera swoje działanie na rozbudowanej sieci takich usług, od prostego serwera plików po zaawansowane platformy chmurowe. Zrozumienie natury usług sieciowych wymaga spojrzenia na nie przez pryzmat warstw abstrakcji, które oddzielają szczegóły implementacyjne od interfejsu udostępnianego klientom.

Współczesne usługi sieciowe korzystają najczęściej z architektury REST, która wykorzystuje protokół HTTP do przesyłania danych w formacie JSON lub XML. Dzięki temu programiści mogą tworzyć aplikacje w dowolnym języku programowania, mając pewność, że będą one w stanie ze sobą współdziałać. Kluczowym aspektem jest również bezpieczeństwo takich usług, które realizuje się poprzez uwierzytelnianie, autoryzację oraz szyfrowanie połączeń.

Warto podkreślić, że usługi sieciowe stanowią fundament dla bardziej złożonych koncepcji, takich jak komputacja rozproszona czy systemy mikroserwisowe. Bez solidnego zrozumienia tej podstawowej warstwy trudno jest budować niezawodne i skalowalne systemy produkcyjne, które są tematem niniejszego wykładu.

2/50
Znaczenie architektury w IT
  • Architektura w technologiach informatycznych (IT) to strukturalny projekt systemu, który definiuje jego komponenty, ich wzajemne relacje oraz zasady rządzące ich projektowaniem i ewolucją.
  • Jest to plan, który zapewnia, że system będzie spełniał wymagania biznesowe, takie jak wydajność, bezpieczeństwo i skalowalność.
  • Dobrze zaprojektowana architektura jest kluczowa dla sukcesu projektu, ponieważ minimalizuje ryzyko techniczne, obniża koszty utrzymania i ułatwia przyszły rozwój.
  • Bez solidnych fundamentów architektonicznych systemy stają się chaotyczne, trudne do modyfikacji i podatne na awarie, co generuje tzw. dług technologiczny.
Ilustracja dla slajdu 2

Architektura w IT pełni funkcję podobną do planu budynku – określa, z jakich elementów składa się system, jak te elementy są ze sobą połączone i jakie reguły nimi rządzą. Jest to kluczowa dziedzina, ponieważ to właśnie decyzje architektoniczne mają największy wpływ na koszty utrzymania, tempo rozwoju i niezawodność systemu w dłuższej perspektywie. Ignorowanie architektury prowadzi do powstawania trudnych w utrzymaniu systemów, w których każda zmiana jest ryzykowna i kosztowna.

Rozróżniamy kilka poziomów architektury: architekturę przedsiębiorstwa (Enterprise Architecture), która obejmuje całą organizację, architekturę systemów (System Architecture), skupiającą się na pojedynczym systemie, oraz architekturę oprogramowania (Software Architecture), dotyczącą wewnętrznej struktury kodu. Każdy z tych poziomów wymaga innych kompetencji i perspektywy. Architekt musi umieć balansować pomiędzy wymaganiami biznesowymi a ograniczeniami technicznymi.

Dobrze udokumentowana i przemyślana architektura jest inwestycją, która zwraca się z nawiązką w trakcie całego cyklu życia systemu. Pozwala na sprawną komunikację w zespole, ułatwia onboardowanie nowych programistów i minimalizuje ryzyko popełnienia kosztownych błędów projektowych na wczesnym etapie.

3/50
Warstwowy model usług
Model warstwowy to jedna z najpopularniejszych koncepcji w projektowaniu architektury oprogramowania, polegająca na podziale systemu na logiczne, poziome warstwy.
  • Każda warstwa ma ściśle określony zakres odpowiedzialności i komunikuje się tylko z warstwami bezpośrednio sąsiadującymi.
  • Typowy model trójwarstwowy obejmuje warstwę prezentacji (interfejs użytkownika), warstwę logiki biznesowej (przetwarzanie danych) oraz warstwę dostępu do danych (komunikacja z bazą danych).
  • Taki podział ułatwia zarządzanie złożonością systemu, promuje wielokrotne użycie komponentów i pozwala na niezależny rozwój poszczególnych warstw przez różne zespoły.
Ilustracja dla slajdu 3

Model warstwowy to jedna z najstarszych i najbardziej sprawdzonych koncepcji w informatyce, która pozwala radzić sobie ze złożonością systemów poprzez ich dekompozycję. Każda warstwa stanowi pewien poziom abstrakcji, ukrywając szczegóły implementacyjne przed warstwami nadrzędnymi. Dzięki temu programista pracujący nad warstwą prezentacji nie musi martwić się o to, w jaki sposób dane są przechowywane na dysku.

Przykładem doskonale znanego modelu warstwowego jest model OSI (Open Systems Interconnection), który dzieli komunikację sieciową na siedem warstw, od fizycznej po aplikacyjną. W kontekście usług sieciowych najczęściej spotykamy się z modelem trójwarstwowym, który oddziela interfejs użytkownika od logiki biznesowej i warstwy danych. Takie podejście nie tylko ułatwia zarządzanie kodem, ale także zwiększa bezpieczeństwo, ponieważ warstwa danych nie jest bezpośrednio dostępna z zewnątrz.

W architekturach mikroserwisowych model warstwowy ewoluuje w kierunku dekompozycji pionowej, gdzie każda usługa posiada własne warstwy, co dodatkowo zwiększa autonomię i możliwość niezależnego wdrażania komponentów systemu.

4/50
Monolity vs mikroserwisy
  • Architektura monolityczna to tradycyjne podejście, w którym cała aplikacja jest budowana jako jedna, spójna jednostka.
  • Wszystkie jej funkcje, takie jak interfejs użytkownika, logika biznesowa i dostęp do danych, są ze sobą ściśle powiązane i wdrażane jako pojedynczy program.
  • W przeciwieństwie do tego, architektura mikroserwisów polega na dekompozycji aplikacji na zbiór małych, niezależnych usług, z których każda odpowiada za konkretną funkcję biznesową.
  • Mikroserwisy komunikują się ze sobą za pomocą lekkich protokołów, najczęściej przez API.
  • Choć monolity są prostsze na starcie, mikroserwisy oferują większą elastyczność, skalowalność i odporność na awarie w dużych, złożonych systemach.
Miejsce na ilustracje

Wybór między architekturą monolityczną a mikroserwisami to jedna z kluczowych decyzji, przed którymi staje każdy zespół projektowy. Monolit, choć często krytykowany, ma swoje zalety: jest prostszy w implementacji na wczesnym etapie projektu, łatwiej go debugować i testować, a także nie wymaga zaawansowanej orkiestracji. Dla małych i średnich aplikacji, zwłaszcza tych o stabilnym i przewidywalnym obciążeniu, monolit może być w pełni wystarczającym rozwiązaniem.

Mikroserwisy z kolei sprawdzają się tam, gdzie kluczowa jest skalowalność, niezależność wdrożeniowa i możliwość równoległej pracy wielu zespołów nad różnymi częściami systemu. Każdy mikroserwis jest samodzielną jednostką biznesową, którą można rozwijać, testować i wdrażać niezależnie od pozostałych. Taka architektura wymaga jednak dojrzałości organizacyjnej i narzędzi do zarządzania złożonością, takich jak konteneryzacja, kolejki komunikatów i zaawansowany monitoring.

Coraz popularniejszym rozwiązaniem pośrednim jest architektura modułowego monolitu, która łączy prostotę wdrożenia monolitu z wewnętrzną organizacją charakterystyczną dla mikroserwisów.

5/50
Architektura klient-serwer
Ten model jest podstawą działania większości usług internetowych, od przeglądania stron WWW, gdzie przeglądarka jest klientem, a serwer WWW dostarcza stronę, po aplikacje mobilne komunikujące się z centralnym systemem.
  • Model klient-serwer to fundamentalna architektura sieciowa, w której zadania i zasoby są rozdzielone między dostawców usług (serwery) a odbiorców usług (klienci).
  • Klient to aplikacja, która inicjuje komunikację, wysyłając żądanie do serwera w celu uzyskania dostępu do danych lub wykonania określonej operacji.
  • Serwer, będący potężniejszą maszyną, pasywnie nasłuchuje na żądania od klientów, przetwarza je i zwraca odpowiedź.
Ilustracja dla slajdu 5

Model klient-serwer jest fundamentem, na którym opiera się większość współczesnych aplikacji sieciowych. W tym modelu serwer jest programem (lub zestawem programów), który oferuje pewne usługi, a klient jest programem, który z tych usług korzysta. Klient inicjuje połączenie, wysyłając żądanie, a serwer na nie odpowiada. Taki podział ról jest naturalny i intuicyjny, co przyczyniło się do ogromnej popularności tego wzorca.

W praktyce jeden serwer może obsługiwać wielu klientów jednocześnie, co rodzi wyzwania związane z zarządzaniem równoległymi połączeniami i zapewnieniem odpowiedniej wydajności. Do obsługi wielu żądań serwery wykorzystują mechanizmy takie jak wielowątkowość, pulowanie połączeń czy asynchroniczne przetwarzanie wejścia-wyjścia. Serwer może być również skalowany poprzez dodawanie kolejnych instancji i stosowanie load balancera.

Współczesne implementacje modelu klient-serwer rzadko są tak proste, jak opisane na slajdzie. Często mamy do czynienia z topologią wielowarstwową, w której pomiędzy klientem a serwerem znajdują się dodatkowe elementy, takie jak proxy, firewalle czy bramy API.

6/50
Architektura trójwarstwowa
  • Architektura trójwarstwowa jest specyficznym i bardzo popularnym wariantem modelu warstwowego, który dzieli aplikację na trzy logiczne i fizyczne części.
  • Pierwsza to warstwa prezentacji, odpowiedzialna za interfejs użytkownika i interakcję z nim, działająca np. w przeglądarce internetowej.
  • Druga to warstwa aplikacji (lub logiki biznesowej), która koordynuje działanie aplikacji, przetwarza dane i implementuje reguły biznesowe.
  • Trzecia to warstwa danych, składająca się z systemu zarządzania bazą danych, który przechowuje i zarządza informacjami.
  • Taki podział pozwala na niezależne skalowanie i rozwijanie każdej z warstw w odpowiedzi na zmieniające się potrzeby.
Ilustracja dla slajdu 6

Architektura trójwarstwowa jest szczególnym przypadkiem modelu warstwowego, który zdominował świat aplikacji korporacyjnych na wiele lat. Jej popularność wynika z przejrzystego podziału odpowiedzialności: warstwa prezentacji odpowiada za interakcję z użytkownikiem, warstwa logiki biznesowej za przetwarzanie reguł biznesowych, a warstwa danych za trwałe przechowywanie informacji. Taki podział pozwala na specjalizację zespołów i wymianę poszczególnych warstw bez wpływu na pozostałe.

W warstwie prezentacji najczęściej spotykamy technologie takie jak Angular, React czy Vue.js, które działają w przeglądarce klienta. Warstwa logiki biznesowej implementowana jest najczęściej w technologiach takich jak Java EE, .NET Core, Node.js czy Python z odpowiednimi frameworkami. Warstwa danych to najczęściej relacyjne bazy danych, takie jak PostgreSQL, MySQL czy Oracle, ale coraz częściej również bazy NoSQL, takie jak MongoDB.

Współczesne implementacje trójwarstwowe często wzbogacane są o dodatkowe komponenty, takie jak systemy cache'owania (Redis), kolejki zadań (RabbitMQ) czy warstwy API Gateway, które dodają funkcjonalności bezpieczeństwa i routingu.

7/50
SOA i jej rola
  • Architektura zorientowana na usługi (SOA, Service-Oriented Architecture) to styl projektowania oprogramowania, w którym funkcje aplikacji są udostępniane jako zbiór niezależnych, luźno powiązanych usług.
  • W przeciwieństwie do mikroserwisów, usługi w SOA są często większe i mogą obejmować szerszy zakres funkcjonalności biznesowej.
  • Kluczową koncepcją w SOA jest Enterprise Service Bus (ESB), czyli magistrala usług, która pośredniczy w komunikacji, transformacji danych i routingu między usługami. SOA miała na celu integrację dużych, heterogenicznych systemów korporacyjnych, promując reużywalność i elastyczność w skali całej organizacji.
Ilustracja dla slajdu 7

Architektura SOA pojawiła się jako odpowiedź na problemy związane z integracją różnorodnych systemów w dużych przedsiębiorstwach. Podstawowym założeniem SOA jest wielokrotne użycie usług w różnych kontekstach biznesowych. Usługi w SOA są projektowane jako niezależne jednostki funkcjonalne, które można łączyć w większe procesy biznesowe za pomocą orkiestracji lub choreografii.

Kluczowym elementem SOA jest ESB (Enterprise Service Bus), który pełni rolę centralnego pośrednika w komunikacji między usługami. ESB odpowiada za transformację protokołów, routowanie wiadomości, walidację danych oraz zarządzanie transakcjami. W praktyce ESB często stawał się wąskim gardłem systemu i pojedynczym punktem awarii, co było jedną z przyczyn odejścia od SOA na rzecz mikroserwisów.

Warto zauważyć, że SOA i mikroserwisy nie są wzajemnie wykluczające. Mikroserwisy można postrzegać jako bardziej dopracowaną i lekką wersję SOA, która rezygnuje z ciężkiego ESB na rzecz prostych, bezpośrednich połączeń i lekkich protokołów komunikacyjnych.

8/50
Komunikacja między usługami
  • Efektywna komunikacja między usługami jest kręgosłupem systemów rozproszonych.
  • Istnieją dwa główne modele tej komunikacji: synchroniczny i asynchroniczny.
  • W komunikacji synchronicznej, usługa wysyłająca żądanie blokuje swoje działanie i czeka na odpowiedź, co jest typowe dla protokołów takich jak HTTP w wywołaniach REST API.
  • Komunikacja asynchroniczna polega na wysłaniu komunikatu bez oczekiwania na natychmiastową odpowiedź, co realizuje się za pomocą systemów kolejkowych, takich jak RabbitMQ czy Kafka.
  • Wybór odpowiedniego modelu zależy od wymagań systemu – synchroniczny jest prostszy, ale asynchroniczny zapewnia większą odporność na awarie i lepszą skalowalność.
Miejsce na ilustracje

Komunikacja między usługami jest jednym z największych wyzwań w systemach rozproszonych. Wybór między komunikacją synchroniczną a asynchroniczną ma fundamentalny wpływ na takie cechy systemu, jak wydajność, niezawodność i skalowalność. W komunikacji synchronicznej usługa wysyłająca żądanie blokuje się, czekając na odpowiedź, co jest proste w implementacji, ale może prowadzić do kaskadowego blokowania się zasobów.

Komunikacja asynchroniczna opiera się na przesyłaniu komunikatów za pośrednictwem pośrednika, takiego jak broker wiadomości (RabbitMQ, Apache Kafka). Dzięki temu usługi są od siebie luźniej zależne i mogą działać niezależnie, nawet jeśli niektóre z nich są tymczasowo niedostępne. Asynchroniczność wprowadza jednak dodatkową złożoność – trzeba radzić sobie z takimi wyzwaniami, jak zapewnienie kolejności wiadomości, obsługa duplikatów i gwarancja dostarczenia.

W praktyce większość systemów stosuje podejście hybrydowe, używając komunikacji synchronicznej dla operacji wymagających natychmiastowej odpowiedzi i asynchronicznej dla zadań długotrwałych lub takich, które nie wymagają natychmiastowego potwierdzenia.

9/50
Topologie sieciowe
  • Topologia sieciowa opisuje fizyczny lub logiczny układ połączeń między urządzeniami w sieci.
  • Do najpopularniejszych topologii fizycznych należą magistrala, gwiazda, pierścień i siatka (mesh).
  • Topologia magistrali, historycznie używana w sieciach Ethernet, polegała na podłączeniu wszystkich urządzeń do jednego kabla.
  • Dziś dominuje topologia gwiazdy, gdzie każde urządzenie łączy się z centralnym punktem, takim jak przełącznik, co zwiększa niezawodność.
  • Topologia siatki, w której urządzenia mogą mieć wiele połączeń między sobą, oferuje najwyższą odporność na awarie i jest często stosowana w sieciach szkieletowych i bezprzewodowych.
Ilustracja dla slajdu 9

Topologia sieciowa ma ogromny wpływ na takie właściwości sieci, jak niezawodność, przepustowość, koszt i łatwość zarządzania. Wybór odpowiedniej topologii zależy od wielu czynników, w tym od wielkości sieci, wymagań dotyczących niezawodności oraz budżetu. Współczesne sieci rzadko opierają się na jednej topologii – częściej mamy do czynienia z topologiami hybrydowymi, które łączą zalety różnych rozwiązań.

Topologia gwiazdy, dominująca w dzisiejszych sieciach lokalnych, ma tę zaletę, że awaria jednego urządzenia nie wpływa na działanie pozostałych. Centralny przełącznik (switch) zarządza ruchem, zapewniając wydajną komunikację między wszystkimi urządzeniami w sieci. Wadą tej topologii jest uzależnienie całej sieci od działania centralnego punktu, który staje się pojedynczym punktem awarii.

Topologia siatki, stosowana w sieciach szkieletowych i bezprzewodowych, oferuje najwyższy poziom niezawodności, ponieważ istnieje wiele alternatywnych ścieżek przesyłania danych. W praktyce często stosuje się topologię częściowej siatki, która zapewnia dobrą niezawodność przy niższych kosztach niż pełna siatka.

10/50
Segmentacja sieci
  • Segmentacja sieci to praktyka dzielenia dużej sieci komputerowej na mniejsze, odizolowane podsieci, zwane segmentami.
  • Celem segmentacji jest poprawa bezpieczeństwa, wydajności i zarządzania siecią.
  • Izolując ruch w ramach poszczególnych segmentów, można ograniczyć zasięg ewentualnego ataku lub awarii, uniemożliwiając jego rozprzestrzenienie się na całą infrastrukturę.
  • Segmentacja zmniejsza również natężenie ruchu rozgłoszeniowego, co prowadzi do poprawy ogólnej wydajności.
  • Najczęściej do realizacji segmentacji wykorzystuje się wirtualne sieci lokalne (VLAN) oraz zapory sieciowe (firewalle) kontrolujące ruch między segmentami.
Ilustracja dla slajdu 10

Segmentacja sieci opiera się na wykorzystaniu technologii VLAN (Virtual Local Area Network), która umożliwia logiczne grupowanie urządzeń niezależnie od ich fizycznego położenia w sieci. Każdy VLAN tworzy odrębną domenę rozgłoszeniową, co oznacza, że ruch broadcastowy generowany w jednym segmencie nie jest przesyłany do pozostałych, co znacząco redukuje zbędny ruch i poprawia wydajność. Implementacja VLAN wymaga odpowiedniej konfiguracji przełączników zarządzalnych oraz stosowania protokołu IEEE 802.1Q do tagowania ramek Ethernet.

W zaawansowanych środowiskach produkcyjnych segmentacja realizowana jest często z użyciem mikrosegmentacji, która pozwala na tworzenie niezwykle wąskich stref bezpieczeństwa wokół pojedynczych obciążeń. W tym przypadku polityki bezpieczeństwa definiowane są na poziomie poszczególnych procesów lub kontenerów, a nie całych podsieci. Narzędzia takie jak VMware NSX, Cisco ACI czy technologie oparte na eBPF i Cilium w środowisku Kubernetes umożliwają implementację mikrosegmentacji na poziomie warstwy trzeciej i wyższych.

Segmentacja sieci znajduje zastosowanie nie tylko w bezpieczeństwie, ale również w optymalizacji zarządzania pasmem i jakością usług (QoS). W sieciach korporacyjnych często wydziela się osobne segmenty dla ruchu głosowego (VoIP), administracyjnego i produkcyjnego, co pozwala na priorytetyzację ruchu krytycznego i zagwarantowanie odpowiedniej przepustowości. Dodatkowo segmentacja ułatwia audyt i zgodność z regulacjami poprzez dokładne kontrolowanie, które systemy mogą komunikować się między sobą.

11/50
Strefy bezpieczeństwa
  • Strefy bezpieczeństwa to logiczne rozwinięcie koncepcji segmentacji sieci, gdzie segmenty są grupowane na podstawie poziomu zaufania i wymagań bezpieczeństwa.
  • Typowy podział obejmuje strefę niezaufaną (np. publiczny Internet), strefę zaufaną (sieć wewnętrzna korporacyjna) oraz strefy o ograniczonym zaufaniu, takie jak DMZ.
  • Każda strefa ma jasno zdefiniowane polityki bezpieczeństwa, które określają, jaki ruch może do niej wchodzić i z niej wychodzić.
  • Ruch pomiędzy strefami jest ściśle kontrolowany przez zapory sieciowe, co pozwala na wdrożenie zasady obrony w głąb.
Ilustracja dla slajdu 11

Strefy bezpieczeństwa odwzorowują model zaufania hierarchicznego, w którym każda strefa posiada przypisany poziom zaufania i odpowiadające mu restrykcje. Podstawowy podział obejmuje strefę o niskim zaufaniu (Internet), strefę o średnim zaufaniu (DMZ), strefę o wysokim zaufaniu (sieć wewnętrzna) oraz strefy o najwyższym zaufaniu (np. systemy księgowe z danymi wrażliwymi). Ruch pomiędzy strefami jest kontrolowany przez firewall wielowarstwowy, który analizuje nie tylko adresy IP i porty, ale również kontekst aplikacyjny i stan sesji.

W praktyce projektowej często wykorzystuje się architekturę wielowarstwowej strefy DMZ, składającej się z DMZ zewnętrznej, DMZ wewnętrznej i właściwej sieci wewnętrznej. Taka topologia umożliwia stopniowe obniżanie poziomu zaufania i tworzenie barier bezpieczeństwa na każdej granicy. Serwery aplikacyjne umieszcza się w DMZ wewnętrznej, która ma dostęp do sieci wewnętrznej tylko na ściśle określonych portach i protokołach, co znacząco utrudnia potencjalnemu atakującemu przemieszczanie się w głąb infrastruktury.

Projektowanie stref bezpieczeństwa wymaga również uwzględnienia przepływu ruchu zarządzającego, logów systemowych i kopii zapasowych. Każda strefa powinna mieć wydzieloną osobną ścieżkę dostępu administracyjnego, najlepiej z wykorzystaniem serwerów przesiadkowych (jump host) z uwierzytelnianiem wieloskładnikowym. Ponadto niezbędne jest zapewnienie redundantnych firewalli w konfiguracji active-passive lub active-active, aby uniknąć pojedynczego punktu awarii na granicach między strefami.

12/50
DMZ – koncepcja i praktyka
Komunikacja z DMZ do sieci wewnętrznej jest ściśle ograniczona i kontrolowana przez zaporę sieciową, co czyni DMZ kluczowym elementem bezpiecznej architektury sieciowej.
  • Strefa zdemilitaryzowana (DMZ, Demilitarized Zone) to specjalny segment sieci, który znajduje się pomiędzy siecią wewnętrzną (zaufaną) a siecią zewnętrzną (niezaufaną), taką jak Internet.
  • Jej celem jest umieszczanie usług, które muszą być dostępne publicznie, na przykład serwerów WWW, pocztowych czy DNS, w odizolowanym środowisku.
  • Dzięki temu, nawet jeśli atakujący zdoła skompromitować serwer w DMZ, nie uzyska on bezpośredniego dostępu do krytycznych zasobów w sieci wewnętrznej.
Miejsce na ilustracje

Strefa DMZ w klasycznej implementacji wykorzystuje architekturę z dwoma zaporami sieciowymi (firewall) tworzącymi tak zwaną podsieć odizolowaną. Pierwszy firewall (front-end) oddziela DMZ od Internetu, przepuszczając jedynie ruch przeznaczony dla publicznych usług, takich jak HTTP i HTTPS do serwera WWW. Drugi firewall (back-end) oddziela DMZ od sieci wewnętrznej, blokując wszelkie połączenia inicjowane z DMZ do wnętrza, chyba że są to odpowiedzi na wcześniej zainicjowane zapytania z sieci wewnętrznej.

W środowiskach chmurowych DMZ implementuje się odmiennie niż w sieciach tradycyjnych – zamiast fizycznych firewalli stosuje się grupy bezpieczeństwa (security groups) i listy kontroli dostępu do sieci (NACL) w połączeniu z wirtualnymi sieciami prywatnymi (VPC). W AWS typowa architektura DMZ obejmuje publiczną podsieć (public subnet) dla load balancera i serwerów WWW oraz prywatną podsieć (private subnet) dla serwerów aplikacyjnych i baz danych. Taka separacja zapewnia, że bazy danych nigdy nie mają bezpośredniego interfejsu publicznego.

DMZ może być również rozszerzona o tak zwaną strefę zarządzania (management DMZ), w której umieszcza się systemy monitoringu, bastion hosty i serwery konfiguracyjne. Strefa ta ma ograniczony dostęp z sieci wewnętrznej dla administratorów, a jednocześnie musi mieć możliwość łączenia się ze wszystkimi zarządzanymi systemami w celu zbierania logów i dystrybucji aktualizacji. Takie podejście zapewnia ścisłą kontrolę nad dostępem administracyjnym i pozwala na pełne audytowanie wszystkich działań zarządczych.

13/50
Redundancja usług
Redundancja jest podstawą budowania systemów o wysokiej dostępności i minimalizuje ryzyko przestojów, które mogą prowadzić do strat finansowych i utraty reputacji.
  • Redundancja to praktyka polegająca na duplikowaniu krytycznych komponentów systemu lub funkcji w celu zwiększenia jego niezawodności i odporności na awarie.
  • W kontekście usług sieciowych oznacza to uruchomienie wielu instancji tej samej usługi, serwera, bazy danych czy urządzenia sieciowego.
  • Jeśli jeden z komponentów ulegnie awarii, jego zduplikowany odpowiednik może natychmiast przejąć jego zadania, zapewniając ciągłość działania.
Ilustracja dla slajdu 13

Redundancja usług jest realizowana na wielu poziomach architektury, począwszy od warstwy fizycznej, gdzie stosuje się zdublowane zasilacze UPS, agregaty prądotwórcze i redundantne łącza sieciowe w topologii full-mesh. Na poziomie serwerowym redundancja przyjmuje postać klastrów typu active-passive lub active-active, w których wiele węzłów jest gotowych do przejęcia obciążenia w razie awarii któregokolwiek z nich. W przypadku baz danych stosuje się replikację synchroniczną lub asynchroniczną z mechanizmami automatycznej promocji repliki do roli primary.

Efektywna redundancja wymaga stosowania wzorca projektowego health check, czyli regularnego monitorowania stanu każdego komponentu poprzez wysyłanie zapytań testowych. System orkiestracji lub load balancer musi w czasie rzeczywistym podejmować decyzje o wyłączeniu uszkodzonych instancji z puli ruchu. W zaawansowanych systemach stosuje się również predykcyjną analizę trendów wydajnościowych, która pozwala na proaktywne uruchamianie dodatkowych instancji zanim awaria faktycznie wystąpi.

Należy pamiętać, że redundancja sama w sobie zwiększa złożoność operacyjną i koszty infrastruktury, ponieważ każdy zdublowany komponent wymaga utrzymania, aktualizacji i monitorowania. Dlatego kluczowym zadaniem architekta jest znalezienie równowagi między poziomem redundancji a kosztami jej utrzymania, co często wyraża się w umowach SLA (Service Level Agreement) określających wymagany procent dostępności usługi. W praktyce stosuje się zasadę N+1, czyli posiadanie jednego zapasowego komponentu więcej niż wynosi minimalne zapotrzebowanie.

14/50
Skalowanie poziome
To podejście jest fundamentem nowoczesnych, chmurowych architektur, ponieważ pozwala na elastyczne i niemal nieograniczone zwiększanie mocy obliczeniowej w odpowiedzi na rosnące obciążenie.
  • Skalowanie poziome (horizontal scaling lub scale-out) polega na zwiększaniu wydajności systemu poprzez dodawanie kolejnych maszyn lub węzłów do istniejącej puli zasobów.
  • Zamiast ulepszać pojedynczy serwer, dodajemy kolejne, równolegle działające serwery, a ruch jest między nimi rozdzielany za pomocą load balancera.
  • Skalowanie poziome jest bardziej elastyczne i często bardziej opłacalne niż skalowanie pionowe, a także naturalnie wspiera redundancję.
Ilustracja dla slajdu 14

Skalowanie poziome wymaga, aby aplikacja była zaprojektowana jako bezstanowa (stateless), czyli nie przechowywała żadnych danych sesyjnych lokalnie na serwerze. Wszelkie informacje o stanie użytkownika muszą być przechowywane w zewnętrznym magazynie, takim jak Redis, Memcached lub w bazie danych. Dzięki temu każda instancja aplikacji może obsłużyć dowolne żądanie, a load balancer może rozdzielać ruch według dowolnego algorytmu, na przykład round-robin, least connections czy weighted distribution, bez ryzyka utraty kontekstu sesji.

W praktyce skalowanie poziome najczęściej implementuje się za pomocą automatycznego skalowania (auto-scaling) w środowiskach chmurowych, takich jak AWS Auto Scaling Groups, Google Instance Groups czy Azure Scale Sets. Definiuje się polityki skalowania, które określają metryki wyzwalające dodanie lub usunięcie instancji, na przykład średnie obciążenie CPU powyżej 70% przez 5 minut. Ważnym aspektem jest również skalowanie w dół (scale-in), które pozwala na oszczędność kosztów poprzez automatyczne wyłączanie niepotrzebnych instancji w okresach niskiego ruchu.

Kluczowym wyzwaniem w skalowaniu poziomym jest zarządzanie stanem rozproszonym, szczególnie w przypadku baz danych i systemów wymagających silnej spójności. Podczas gdy bezstanowa warstwa aplikacyjna skaluje się łatwo, warstwa danych wymaga bardziej zaawansowanych technik, takich jak sharding, partycjonowanie czy replikacja master-slave. Dlatego w architekturach mikroserwisowych często stosuje się podejście CQRS (Command Query Responsibility Segregation), które rozdziela operacje zapisu i odczytu, umożliwiając niezależne skalowanie każdego z tych obciążeń.

15/50
Skalowanie pionowe
  • Skalowanie pionowe (vertical scaling lub scale-up) to metoda zwiększania wydajności systemu poprzez dodawanie zasobów do pojedynczej maszyny.
  • Oznacza to na przykład zwiększenie ilości pamięci RAM, dodanie mocniejszego procesora (CPU) lub zainstalowanie szybszych dysków twardych.
  • Jest to prostsze w implementacji niż skalowanie poziome, ponieważ nie wymaga zmian w architekturze aplikacji.
  • Jednakże, skalowanie pionowe ma swoje granice – w pewnym momencie nie da się już bardziej rozbudować pojedynczego serwera, a koszty takiej rozbudowy rosną wykładniczo.
  • Ponadto, nie zapewnia ono redundancji – awaria pojedynczej, potężnej maszyny powoduje przestój całego systemu.
Ilustracja dla slajdu 15

Skalowanie pionowe jest często pierwszym wyborem w przypadku starszych systemów monolitycznych lub aplikacji, które nie zostały zaprojektowane z myślą o architekturze rozproszonej. Wymaga ono przestoju serwera podczas rozbudowy, chyba że zastosuje się technologie hot-swap, które pozwalają na wymianę komponentów bez wyłączania maszyny. W środowiskach wirtualizowanych skalowanie pionowe jest znacznie prostsze – wystarczy zmienić parametry maszyny wirtualnej (vCPU, RAM) i uruchomić ponownie system, co zajmuje zaledwie kilka minut.

Ograniczenia skalowania pionowego wynikają z fizycznych i architektonicznych barier sprzętowych. Każda płyta główna ma limitowaną liczbę gniazd procesora i slotów pamięci RAM, a przekroczenie pewnego progu gęstości komponentów wymaga przejścia na droższe platformy serwerowe klasy enterprise, takie jak serwery z architekturą NUMA (Non-Uniform Memory Access). W praktyce oznacza to, że koszt jednostkowy mocy obliczeniowej rośnie wykładniczo powyżej pewnego punktu, czyniąc skalowanie poziome bardziej opłacalnym dla dużych obciążeń.

Mimo swoich wad, skalowanie pionowe pozostaje atrakcyjne w scenariuszach, w których licencjonowanie oprogramowania opiera się na liczbie rdzeni lub instancji. W przypadku baz danych Oracle, Microsoft SQL Server czy niektórych systemów ERP tańsze może być posiadanie jednej potężnej maszyny zamiast kilkunastu mniejszych, ponieważ koszty licencji skalują się liniowo z liczbą rdzeni. Dodatkowo skalowanie pionowe eliminuje problemy związane ze spójnością danych rozproszonych i upraszcza architekturę, co zmniejsza koszty operacyjne.

16/50
Load balancery
Load balancer (równoważnik obciążenia) to kluczowy komponent w architekturach skalowalnych i o wysokiej dostępności.
  • Jest to urządzenie lub oprogramowanie, które rozdziela przychodzący ruch sieciowy na wiele serwerów docelowych.
  • Dzięki temu żaden pojedynczy serwer nie jest przeciążony, co zapewnia optymalną wydajność i czas odpowiedzi.
  • Load balancery monitorują również stan serwerów w swojej puli i automatycznie przestają kierować ruch do tych, które uległy awarii.
  • W ten sposób zapewniają one nie tylko skalowalność, ale także wysoką dostępność, maskując awarie pojedynczych węzłów przed użytkownikami końcowymi.
Miejsce na ilustracje

Load balancery działają na różnych warstwach modelu OSI, co determinuje zakres ich funkcjonalności. Najpopularniejsze są load balancery warstwy 4 (transportowej), które podejmują decyzje routingu na podstawie adresów IP i portów TCP lub UDP, oraz warstwy 7 (aplikacyjnej), które analizują zawartość pakietów, takie jak nagłówki HTTP, ciasteczka czy parametry URL. Te drugie umożliwiają zaawansowane strategie routingu, na przykład kierowanie ruchu do różnych serwerów w zależności od ścieżki URL lub typu urządzenia klienckiego.

Algorytmy równoważenia obciążenia decydują o sposobie dystrybucji ruchu między serwery. Najprostszy algorytm round-robin przydziela żądania w cyklicznej kolejności, ale nie uwzględnia różnic w wydajności poszczególnych serwerów. Bardziej zaawansowane algorytmy, takie jak least connections (kieruje ruch do serwera z najmniejszą liczbą aktywnych połączeń) czy weighted response time (uwzględnia średni czas odpowiedzi), pozwalają na optymalne wykorzystanie zasobów w heterogenicznych środowiskach. W chmurze często stosuje się algorytmy oparte na przepustowości (throughput) lub geoproximity.

Współczesne load balancery oferują również funkcje bezpieczeństwa, takie jak ochrona przed atakami DDoS, szyfrowanie SSL/TLS (terminacja SSL), filtrowanie żądań według reguł WAF (Web Application Firewall) i rate limiting. Popularne rozwiązania open source, takie jak HAProxy, Nginx i Traefik, zapewniają wysoką wydajność przy niskim zużyciu zasobów, podczas gdy komercyjne rozwiązania sprzętowe, takie jak F5 BIG-IP czy Citrix ADC, oferują dodatkowe funkcje zaawansowane, w tym globalne równoważenie obciążenia (GSLB) między wieloma lokalizacjami.

17/50
HA – podstawy
  • Wysoka dostępność (HA) to cecha systemu informatycznego, która gwarantuje jego nieprzerwane działanie przez określony, bardzo wysoki procent czasu, np. 99,999% (tzw. „pięć dziewiątek").
  • Celem HA jest eliminacja pojedynczych punktów awarii poprzez zastosowanie redundancji na wszystkich poziomach architektury – od zasilania i chłodzenia, przez urządzenia sieciowe, po serwery i aplikacje.
  • Systemy HA są projektowane tak, aby automatycznie wykrywać awarie i przełączać się na zapasowe komponenty w sposób niewidoczny dla użytkownika.
  • Jest to kluczowe dla usług, których przestój generuje krytyczne straty.
Ilustracja dla slajdu 17

Wysoka dostępność systemu mierzona jest za pomocą wskaźnika SLA (Service Level Agreement), który określa dopuszczalny czas przestoju w skali roku. Dla poziomu 99,9% (trzy dziewiątki) dopuszczalny jest przestój wynoszący około 8,7 godziny rocznie, podczas gdy dla 99,999% (pięć dziewiątek) jest to zaledwie 5,26 minuty. Osiągnięcie tak rygorystycznych poziomów wymaga nie tylko redundancji sprzętu i oprogramowania, ale również starannie zaprojektowanych procedur operacyjnych i wykwalifikowanego zespołu DevOps gotowego do natychmiastowej reakcji.

Architektura HA opiera się na kilku kluczowych zasadach: eliminacji pojedynczych punktów awarii (SPOF), niezawodnych mechanizmach detekcji awarii i szybkim przełączaniu awaryjnym (failover). Detekcja awarii realizowana jest przez systemy heartbeat, które wysyłają regularne sygnały (pingi) między węzłami klastra. W przypadku braku odpowiedzi przez określoną liczbę interwałów, uznaje się węzeł za martwy i uruchamia procedurę przejęcia jego zadań przez pozostałe węzły. Ważne jest unikanie sytuacji split-brain, w której dwa węzły jednocześnie uznają się za aktywne.

W praktyce HA najczęściej implementuje się z wykorzystaniem klastrów typu active-passive z mechanizmem floating IP lub klastrów active-active z load balancerem. Popularnym narzędziem do budowy klastrów HA w środowisku Linux jest Pacemaker z Corosync, który zapewnia wykrywanie awarii, zarządzanie zasobami i koordynację działań między węzłami. W chmurze odpowiednikiem są usługi zarządzane, takie jak AWS Auto Scaling z multi-AZ deployment, Azure Availability Sets czy Google Cloud Managed Instance Groups, które automatyzują proces utrzymania wysokiej dostępności na poziomie infrastruktury.

18/50
Failover i failback
Przełączenie awaryjne (failover) to proces automatycznego przełączenia na redundantny, zapasowy system lub komponent w momencie, gdy aktywny (główny) system przestaje działać.
Powrót po awarii (failback) to proces powrotny, polegający na przywróceniu działania na pierwotnym, głównym systemie po jego naprawie.
  • Jest to podstawowy mechanizm zapewniający wysoką dostępność.
  • Po wykryciu awarii, ruch lub zadania są natychmiast przekierowywane do systemu zapasowego, co minimalizuje lub całkowicie eliminuje przerwę w działaniu usługi.
  • Proces powrotu może być automatyczny lub manualny, w zależności od konfiguracji i polityki, aby zapewnić stabilność po powrocie do normalnego trybu pracy.
Ilustracja dla slajdu 18

Mechanizm failover może być realizowany w modelu cold standby, warm standby lub hot standby, różniących się stopniem gotowości systemu zapasowego i czasem przełączenia. W modelu cold standby system zapasowy jest wyłączony i wymaga ręcznego uruchomienia, co skutkuje przestojem liczonym w minutach lub godzinach. Warm standby utrzymuje system zapasowy w stanie gotowości z załadowanym systemem operacyjnym i aplikacją, ale bez aktywnego ruchu. Hot standby utrzymuje pełną synchronizację danych i jest gotowy do natychmiastowego przejęcia ruchu w ciągu kilku sekund.

Poprawna implementacja failback wymaga szczególnej uwagi, ponieważ przywrócenie działania na pierwotnym systemie po naprawie jest często bardziej ryzykowne niż samo przełączenie awaryjne. Podczas failback należy zapewnić spójność danych, które mogły ulec zmianie w systemie zapasowym w trakcie awarii, oraz płynne przejęcie ruchu bez zakłóceń dla użytkowników. W systemach bazodanowych proces ten wymaga synchronizacji wszystkich zaległych transakcji i replikacji zmian z powrotem do systemu głównego przed przełączeniem.

W zaawansowanych architekturach stosuje się zautomatyzowane scenariusze failover i failback z wykorzystaniem narzędzi do orkiestracji, takich jak Ansible, Terraform lub Kubernetes Operators. Scenariusze te są regularnie testowane w ramach ćwiczeń chaos engineering, które polegają na celowym wprowadzaniu awarii w kontrolowanym środowisku w celu weryfikacji poprawności procedur. Dopiero po pomyślnym przejściu wszystkich testów scenariusz failover może być uznany za w pełni niezawodny i bezpieczny do użycia w środowisku produkcyjnym.

19/50
Odporność na awarie
  • Odporność na awarie to zdolność systemu do kontynuowania działania, być może z ograniczoną wydajnością, nawet w przypadku wystąpienia awarii jednego lub więcej jego komponentów.
  • Jest to szersze pojęcie niż wysoka dostępność, ponieważ skupia się na zdolności do przetrwania awarii, a nie tylko na minimalizacji przestojów.
  • Architektury odporne na awarie są projektowane z założeniem, że awarie są nieuniknione.
  • Stosuje się w nich techniki takie jak redundancja, izolacja komponentów oraz mechanizmy samonaprawy, aby system mógł samodzielnie radzić sobie z problemami bez interwencji człowieka.
Ilustracja dla slajdu 19

Architektura odporna na awarie (fault-tolerant architecture) zakłada, że każda awaria jest izolowana i nie wpływa na pozostałe komponenty systemu, co realizuje się poprzez stosowanie wzorców projektowych takich jak bulkhead, circuit breaker i graceful degradation. Wzorzec bulkhead (gródź) polega na podziale zasobów systemu na odizolowane pule, tak aby awaria w jednej części nie doprowadziła do wyczerpania zasobów w innych częściach. Na przykład w aplikacji Java każdy wątek obsługujący określony typ żądań może korzystać z własnej puli wątków, co zapobiega kaskadowemu blokowaniu całego serwera.

Wzorzec circuit breaker (bezpiecznik) jest niezbędnym mechanizmem w systemach rozproszonych, który chroni przed przeciążeniem i propagacją błędów. Gdy liczba nieudanych wywołań zdalnej usługi przekroczy zdefiniowany próg, bezpiecznik rozwiera się i wszystkie kolejne wywołania są natychmiast odrzucane z błędem, bez próby faktycznego połączenia. Po określonym czasie bezpiecznik przechodzi w stan półotwarty i przepuszcza ograniczoną liczbę żądań testowych, aby sprawdzić, czy usługa została przywrócona. Popularne implementacje to Hystrix (Netflix), Resilience4j (Java) i Polly (.NET).

Odporność na awarie wymaga również stosowania strategii graceful degradation, czyli stopniowego obniżania jakości usługi zamiast całkowitego jej zatrzymania w przypadku awarii niektórych komponentów. Przykładem może być serwis e-commerce, który w przypadku niedostępności systemu rekomendacji wyświetla domyślne produkty zamiast spersonalizowanych, lub system płatności, który tymczasowo wyłącza drogie metody płatności. Kluczowym aspektem jest identyfikacja funkcji krytycznych (must-work) i niekrytycznych (nice-to-have) oraz zaprojektowanie systemu tak, aby te pierwsze działały nawet w warunkach awarii.

20/50
Projektowanie dla dostępności
  • Projektowanie dla dostępności to podejście inżynierskie, które od samego początku procesu projektowego uwzględnia wymagania dotyczące niezawodności i ciągłości działania.
  • Zamiast dodawać mechanizmy HA jako „nakładkę" na gotowy system, wbudowuje się je w jego fundamenty.
  • Obejmuje to identyfikację i eliminację pojedynczych punktów awarii, implementację redundancji na wszystkich poziomach, planowanie procedur przełączeń awaryjnych, a także projektowanie usług w taki sposób, aby były bezstanowe, co znacznie ułatwia ich skalowanie i przełączanie w razie awarii.
  • Kluczowe jest również wdrożenie kompleksowego monitoringu, który pozwala proaktywnie wykrywać potencjalne problemy.
Miejsce na ilustracje

Projektowanie dla dostępności rozpoczyna się od analizy wymagań biznesowych i określenia docelowego poziomu SLA, który musi spełniać system. Na tej podstawie definiuje się architekturę referencyjną, która uwzględnia redundancję na każdym poziomie: geograficznym (wiele regionów), dostępowym (wielu dostawców usług internetowych), sieciowym (redundantne przełączniki i routery), obliczeniowym (klastry serwerów) i danych (repliki baz danych). Każdy z tych poziomów musi być zaprojektowany w taki sposób, aby awaria pojedynczego elementu nie wpływała na ogólną dostępność systemu.

W nowoczesnych systemach kluczowym aspektem projektowania dla dostępności jest wykorzystanie architektury mikroserwisowej z dekompozycją na małe, niezależne jednostki, które mogą być wdrażane i skalowane oddzielnie. Należy unikać wspólnych zasobów, takich jak centralne bazy danych czy monolityczne kolejki, które stają się wąskimi gardłami i pojedynczymi punktami awarii. Zamiast tego stosuje się podejście database-per-service, asynchroniczną komunikację przez kolejki zdarzeń i sagi do zarządzania transakcjami rozproszonymi, co zapewnia izolację awarii i autonomię poszczególnych usług.

Nieodzownym elementem projektowania dla dostępności jest wdrożenie kompleksowego systemu monitoringu i alertowania, który obejmuje zarówno metryki infrastrukturalne (CPU, pamięć, dysk, sieć), jak i metryki aplikacyjne (czas odpowiedzi, liczba błędów, przepustowość). Narzędzia takie jak Prometheus, Grafana, ELK Stack czy Datadog umożliwiają wizualizację trendów i szybkie wykrywanie anomalii. Dodatkowo wdrożenie mechanizmów self-healing, które automatycznie restartują uszkodzone procesy lub instancje, pozwala na minimalizację czasu przestoju bez konieczności interwencji człowieka.

21/50
Zależności usług
  • W architekturach rozproszonych, takich jak mikroserwisy, usługi często zależą od siebie nawzajem, tworząc złożoną sieć powiązań.
  • Niewłaściwe zarządzanie tymi zależnościami może prowadzić do kaskadowych awarii, gdzie problem w jednej, pozornie mało istotnej usłudze, paraliżuje działanie całego systemu.
  • Dlatego kluczowe jest mapowanie i rozumienie tych zależności.
  • Stosuje się wzorce projektowe, takie jak "circuit breaker" (bezpiecznik), który w razie problemów z zależnością tymczasowo ją odcina, aby zapobiec eskalacji problemu.
  • Ważne jest również stosowanie luźnych powiązań (loose coupling), aby zmiany w jednej usłudze nie wymuszały natychmiastowych zmian w innych.
Ilustracja dla slajdu 21

Mapowanie zależności między usługami jest kluczowym elementem utrzymania stabilności systemu rozproszonego. W praktyce inżynierskiej stosuje się narzędzia do automatycznego wykrywania i wizualizacji grafu zależności, co pozwala zidentyfikować potencjalne wąskie gardła i punkty krytyczne. Wzorzec circuit breaker, zaimplementowany w bibliotekach takich jak Hystrix czy Resilience4j, monitoruje liczbę nieudanych wywołań i po przekroczeniu progu otwiera obwód, odcinając dalsze żądania do awaryjnej usługi.

Luźne powiązania między usługami osiąga się poprzez kontrakt oparty na interfejsach, a nie na konkretnych implementacjach. W architekturze mikroserwisowej oznacza to, że usługa kliencka zna jedynie interfejs API usługi docelowej i nie zależy od jej wewnętrznych szczegółów. Dodatkowo stosuje się asynchroniczną komunikację przez kolejki komunikatów, która naturalnie zmniejsza zależności czasowe między komponentami.

W dokumentacji technicznej warto tworzyć macierz zależności, która określa, które usługi są wymagane do poprawnego działania każdej funkcji biznesowej. Takie podejście ułatwia planowanie wdrożeń i pozwala przewidzieć skutki potencjalnych awarii. Systematyczna analiza zależności jest niezbędna w procesie capacity planningu i zapewnienia odpowiedniego poziomu SLA dla całej platformy.

22/50
Integracja usług z bazami
  • Sposób, w jaki usługi integrują się z bazami danych, ma ogromny wpływ na architekturę systemu.
  • W architekturze monolitycznej wiele komponentów aplikacji często korzysta z jednej, centralnej bazy danych.
  • W świecie mikroserwisów preferowane jest podejście "database per service" (każda usługa ma własną bazę danych), gdzie każda usługa zarządza własną, prywatną bazą danych.
  • Zapewnia to izolację i autonomię, pozwalając na wybór technologii bazy danych najlepiej dopasowanej do potrzeb danej usługi.
  • Jednakże, takie podejście wprowadza wyzwania związane z utrzymaniem spójności danych między usługami, które rozwiązuje się za pomocą wzorców takich jak "saga" czy "event sourcing".
Ilustracja dla slajdu 22

Wzorzec database per service jest fundamentem izolacji danych w architekturze mikroserwisowej, ponieważ każda usługa posiada własną bazę danych i komunikuje się z nią wyłącznie przez swoje API. Eliminuje to problem wspólnego schematu i pozwala na niezależne skalowanie warstwy przechowywania danych dla każdej usługi. Dodatkowo każda usługa może wykorzystać optymalny dla siebie typ bazy danych, na przykład relacyjną dla danych transakcyjnych i dokumentową dla treści o zmiennej strukturze.

Konsekwencją tego podejścia jest wyzwanie związane z utrzymaniem spójności danych w całym systemie. Wzorzec saga rozwiązuje ten problem poprzez podzielenie transakcji rozproszonej na serię lokalnych transakcji, z których każda jest wykonywana w obrębie pojedynczej usługi. W przypadku niepowodzenia jednej z nich uruchamiane są kompensujące operacje cofające efekty wcześniejszych kroków, co zapewnia ostateczną spójność systemu.

Event sourcing to alternatywna strategia, w której stan systemu jest rekonstruowany na podstawie sekwencji zdarzeń zamiast przechowywania bieżącego stanu w bazie danych. Każda zmiana jest zapisywana jako niezmienny fakt w dzienniku zdarzeń, co daje pełną historię modyfikacji i umożliwia audyt. Takie podejście doskonale sprawdza się w systemach wymagających pełnej śledzalności i możliwości cofnięcia zmian.

23/50
Architektura API
Dobrze zaprojektowane API jest kluczowe dla sukcesu usługi – powinno być spójne, przewidywalne, dobrze udokumentowane i bezpieczne.
Zarządzanie cyklem życia API, jego wersjonowaniem i bezpieczeństwem jest fundamentalnym zadaniem w utrzymaniu usług.
  • API (Application Programming Interface) to kontrakt, który definiuje, w jaki sposób komponenty oprogramowania powinny ze sobą interagować.
  • W kontekście usług sieciowych, API jest bramą, przez którą usługa udostępnia swoje funkcje światu zewnętrznemu.
  • Popularne style architektury API to REST (Representational State Transfer), oparty na standardowych metodach HTTP, oraz GraphQL, który pozwala klientom precyzyjnie określić, jakich danych potrzebują.
Ilustracja dla slajdu 23

REST pozostaje najpopularniejszym stylem architektury API ze względu na swoją prostotę i wykorzystanie standardowych mechanizmów protokołu HTTP. Każdy zasób jest identyfikowany przez URI, a operacje na nim są odwzorowywane na metody HTTP, takie jak GET, POST, PUT i DELETE. Wersjonowanie API jest kluczową praktyką umożliwiającą wprowadzanie zmian bez zrywania kontraktu z istniejącymi klientami, realizowane najczęściej przez prefiks URL lub nagłówek żądania.

GraphQL stanowi elastyczną alternatywę dla REST, pozwalającą klientom na precyzyjne określenie struktury odpowiedzi w zapytaniu. Eliminuje to problemy nadmiarowego lub niedostatecznego pobierania danych, które są typowe dla REST. Implementacja GraphQL wymaga jednak starannego zabezpieczenia przed zbyt złożonymi zapytaniami, które mogłyby przeciążyć serwer, poprzez mechanizmy takie jak limit głębokości zapytań i cost analysis.

Niezależnie od wybranego stylu, dokumentacja API powinna być generowana automatycznie ze specyfikacji, na przykład OpenAPI dla REST lub GraphQL Schema Language dla GraphQL. Automatyczne generowanie eliminuje ryzyko rozbieżności między dokumentacją a rzeczywistą implementacją. Testowanie kontraktów API za pomocą narzędzi takich jak Pact pozwala na wczesne wykrycie niezgodności między usługami w procesie integracji.

24/50
Reverse proxy
Pełni ono wiele kluczowych funkcji w architekturze: może działać jako load balancer, rozdzielając ruch na wiele serwerów; może obsługiwać terminację SSL, odciążając serwery aplikacyjne z zadania szyfrowania i deszyfrowania ruchu; może również pełnić rolę cache, przechowując często żądane zasoby, oraz działać jako zapora aplikacyjna (WAF).
  • Reverse proxy (odwrotne proxy) to serwer, który znajduje się przed serwerami aplikacyjnymi i przekazuje do nich żądania od klientów.
  • Z perspektywy klienta, reverse proxy jest pojedynczym punktem kontaktu z usługą.
  • Popularne implementacje to Nginx, HAProxy czy Apache.
Miejsce na ilustracje

Nginx jest jedną z najpopularniejszych implementacji reverse proxy, cenioną za wysoką wydajność przy obsłudze równoczesnych połączeń dzięki architekturze opartej na zdarzeniach. Serwer ten może obsługiwać dziesiątki tysięcy jednoczesnych połączeń przy minimalnym zużyciu pamięci, co czyni go idealnym wyborem dla systemów o dużym obciążeniu. Terminacja SSL na poziomie reverse proxy odciąża serwery aplikacyjne od kosztownego obliczeniowo szyfrowania i deszyfrowania ruchu.

HAProxy specjalizuje się w zaawansowanym równoważeniu obciążenia, oferując wiele algorytmów dystrybucji ruchu, w tym round robin, least connections i source IP hash. Narzędzie to umożliwia również szczegółowe monitorowanie stanu serwerów w puli za pomocą health checków. W przypadku awarii serwera HAProxy automatycznie wyklucza go z puli i kieruje ruch do pozostałych zdrowych instancji.

Zapora aplikacyjna Web Application Firewall zintegrowana z reverse proxy chroni przed typowymi atakami na aplikacje webowe, takimi jak SQL injection, cross-site scripting i inne zagrożenia z listy OWASP Top 10. Połączenie tych funkcji w jednym komponencie upraszcza architekturę i redukuje opóźnienia. W praktyce produkcyjnej reverse proxy jest często pierwszym punktem styku z ruchem zewnętrznym i stanowi krytyczną warstwę bezpieczeństwa oraz optymalizacji.

25/50
Mechanizmy cache
Jest to jeden z najskuteczniejszych sposobów na poprawę wydajności i skalowalności systemów.
Kluczowym wyzwaniem w cachingu jest zarządzanie unieważnianiem danych, aby użytkownicy nie otrzymywali przestarzałych informacji.
  • Caching (buforowanie) to technika polegająca na przechowywaniu kopii danych w tymczasowej, szybkiej pamięci (cache), aby przyszłe żądania o te same dane mogły być obsłużone szybciej.
  • Cache może być implementowany na wielu poziomach: w przeglądarce klienta, w sieciach dostarczania treści (CDN), na serwerze reverse proxy, a także w samej aplikacji przy użyciu dedykowanych systemów, takich jak Redis czy Memcached.
Ilustracja dla slajdu 25

Redis jest najpopularniejszym systemem cache in-memory, oferującym bogaty zestaw struktur danych, takich jak stringi, listy, zbiory i mapy haszujące. Dzięki przechowywaniu danych w pamięci RAM zapewnia czas odpowiedzi rzędu mikrosekund, co znacząco przyspiesza operacje odczytu. Redis wspiera również zaawansowane funkcje, w tym wygasanie kluczy zdefiniowane przez TTL, transakcje oraz mechanizm Pub/Sub do komunikacji między instancjami aplikacji.

Unieważnianie cache jest najtrudniejszym problemem w projektowaniu systemów buforujących, ponieważ nieaktualne dane mogą prowadzić do błędów biznesowych. Strategia write-through zapisuje dane jednocześnie do cache i bazy danych, co zapewnia spójność kosztem większego opóźnienia przy zapisie. Alternatywnie strategia write-behind zapisuje dane do cache natychmiast, a do bazy danych asynchronicznie, co poprawia wydajność zapisu, ale zwiększa ryzyko utraty danych w przypadku awarii.

Cache dystrybuowany pozwala na współdzielenie bufora między wieloma instancjami tej samej usługi, co eliminuje problem niespójności danych w scenariuszach skalowania poziomego. Memcached jest lżejszą alternatywą dla Redis, oferującą prostszy model danych i niższe narzuty pamięciowe. Wybór odpowiedniego systemu cache powinien być poprzedzony analizą wzorców dostępu do danych i wymagań dotyczących spójności.

26/50
CDN w architekturze usług
  • Sieć dostarczania treści (CDN, Content Delivery Network) to globalnie rozproszona sieć serwerów proxy, która przechowuje kopie statycznych zasobów (takich jak obrazy, pliki CSS i JavaScript) bliżej użytkowników końcowych.
  • Kiedy użytkownik żąda dostępu do zasobu, CDN serwuje go z najbliższego geograficznie serwera (tzw. Edge location), co znacząco skraca czas ładowania i zmniejsza opóźnienia.
  • Włączenie CDN do architektury usług odciąża serwery aplikacyjne, poprawia globalną wydajność i skalowalność, a także może zapewniać dodatkową warstwę ochrony przed atakami DDoS.
Ilustracja dla slajdu 26

Sieć CDN składa się z wielu geograficznie rozproszonych serwerów brzegowych, które przechowują kopie statycznych zasobów aplikacji. Gdy użytkownik żąda zasobu, CDN kieruje zapytanie do najbliższego serwera brzegowego, co minimalizuje opóźnienia sieciowe i przyspiesza ładowanie stron. Popularni dostawcy CDN, tacy jak Cloudflare, Akamai czy Amazon CloudFront, oferują globalną infrastrukturę obejmującą setki lokalizacji na całym świecie.

CDN odciąża serwery pochodzenia, ponieważ większość żądań dotyczących zasobów statycznych jest obsługiwana przez serwery brzegowe bez konieczności kontaktowania się z serwerem aplikacyjnym. Przekłada się to bezpośrednio na niższe koszty przepustowości i mniejsze obciążenie infrastruktury backendowej. Dodatkowo CDN może buforować odpowiedzi dynamiczne przez określony czas, co wymaga starannego ustawienia nagłówków Cache-Control i mechanizmów purge.

Ochrona przed atakami DDoS jest istotną zaletą stosowania CDN, ponieważ sieć brzegowa absorbuje i filtruje złośliwy ruch zanim dotrze on do serwerów aplikacyjnych. CDN oferuje również funkcje przyspieszania ruchu dynamicznego poprzez optymalizację tras sieciowych i protokołów TLS. W architekturze produkcyjnej CDN powinien być skonfigurowany jako pierwsza warstwa w ścieżce żądania, poprzedzająca reverse proxy i serwery aplikacyjne.

27/50
Optymalizacja wydajności
Kluczem do skutecznej optymalizacji jest ciągły monitoring i pomiar kluczowych wskaźników wydajności (KPI).
  • Optymalizacja wydajności to ciągły proces mający na celu poprawę szybkości działania i responsywności systemu przy jednoczesnym minimalizowaniu zużycia zasobów.
  • Obejmuje on szeroki zakres działań na wszystkich poziomach architektury.
  • Na poziomie aplikacji może to być optymalizacja zapytań do bazy danych, profilowanie i refaktoryzacja kodu.
  • Na poziomie infrastruktury obejmuje to strojenie parametrów serwerów, wykorzystanie mechanizmów cache, kompresję danych oraz odpowiedni dobór i konfigurację komponentów, takich jak load balancery czy CDN.
Ilustracja dla slajdu 27

Profilowanie kodu jest fundamentalnym narzędziem w procesie optymalizacji wydajności, pozwalającym zidentyfikować fragmenty kodu, które zużywają najwięcej zasobów. Narzędzia takie jak YourKit, VisualVM czy perf umożliwiają analizę czasu wykonania poszczególnych funkcji i alokacji pamięci. Bez dokładnego profilowania optymalizacja opiera się na domysłach, co często prowadzi do marnowania czasu na ulepszanie elementów, które nie mają istotnego wpływu na ogólną wydajność.

Optymalizacja zapytań do bazy danych obejmuje tworzenie odpowiednich indeksów, analizę planów wykonania zapytań oraz unikanie problemu N+1 zapytań. Indeksy znacząco przyspieszają wyszukiwanie danych, ale ich nadmiar spowalnia operacje zapisu, dlatego kluczowe jest znalezienie właściwej równowagi. W praktyce warto korzystać z narzędzi takich jak EXPLAIN w PostgreSQL czy MySQL do analizy planów wykonania i identyfikacji pełnych skanów tabel.

Monitoring wydajności w środowisku produkcyjnym powinien być realizowany za pomocą systemów APM, takich jak New Relic, Datadog czy OpenTelemetry. Rozproszone śledzenie żądań pozwala na identyfikację opóźnień w łańcuchu wywołań między wieloma usługami. Ustawienie alertów na kluczowe wskaźniki wydajności, takie jak percentyl 95 czasu odpowiedzi i liczba błędów, umożliwia szybką reakcję na degradację systemu zanim wpłynie ona na użytkowników końcowych.

28/50
Buforowanie danych
  • Buforowanie danych (data buffering) to technika używana do tymczasowego przechowywania danych w pamięci podczas ich przenoszenia między dwoma różnymi urządzeniami lub procesami, które działają z różną prędkością.
  • Bufor pomaga w wyrównaniu tych różnic, zapobiegając utracie danych i poprawiając płynność ich przepływu.
  • Przykładem jest buforowanie wideo podczas streamingu, gdzie fragmenty filmu są pobierane z wyprzedzeniem, aby zapewnić płynne odtwarzanie.
  • W architekturze usług bufory, często w postaci systemów kolejkowych, są używane do absorbowania nagłych skoków obciążenia i zapewnienia niezawodnej komunikacji asynchronicznej.
Miejsce na ilustracje

Systemy kolejkowe, takie jak Apache Kafka i RabbitMQ, są najczęściej stosowaną implementacją buforowania w architekturze usług. Kafka działa na zasadzie dziennika komunikatów z podziałem na partycje, co zapewnia wysoką przepustowość i możliwość odtwarzania strumienia danych. RabbitMQ z kolei oferuje zaawansowane wzorce routingu i potwierdzania dostarczenia wiadomości, co jest istotne w systemach wymagających gwarancji dostarczenia.

Buforowanie danych w kolejce pozwala na wyrównanie skoków obciążenia poprzez tymczasowe przechowywanie nadmiarowych żądań, gdy system odbiorczy nie nadąża z ich przetwarzaniem. Mechanizm backpressure informuje nadawcę o konieczności spowolnienia tempa wysyłania komunikatów, co zapobiega przepełnieniu bufora. W praktyce produkcyjnej kluczowe jest monitorowanie rozmiaru kolejek i ustawienie alertów na przekroczenie progów ostrzegawczych.

Buforowanie strumieniowe jest wykorzystywane w systemach przetwarzania danych w czasie rzeczywistym, takich jak Apache Flink czy Apache Spark Streaming. Bufory gromadzą dane przez krótki okres przed ich przetworzeniem w mikro-batchach, co zwiększa przepustowość kosztem niewielkiego opóźnienia. W architekturze usług warto projektować systemy tak, aby były w stanie bezpiecznie obsłużyć sytuację całkowitego zapełnienia bufora, na przykład poprzez implementację polityk odrzucania lub przekierowywania nadmiaru danych.

29/50
Orkiestracja komponentów
  • Orkiestracja to zautomatyzowane zarządzanie, koordynacja i konfiguracja złożonych systemów komputerowych i usług.
  • W kontekście architektur opartych na wielu komponentach, takich jak mikroserwisy czy kontenery, orkiestracja jest niezbędna do zarządzania ich cyklem życia.
  • Narzędzia do orkiestracji, takie jak Kubernetes, automatyzują procesy wdrażania, skalowania, monitorowania i odzyskiwania po awarii.
  • Orkiestrator podejmuje decyzje o tym, gdzie uruchomić dany komponent, jak zapewnić komunikację między nimi i jak reagować na zmiany obciążenia lub awarie, co znacząco upraszcza zarządzanie skomplikowanymi, rozproszonymi środowiskami.
Ilustracja dla slajdu 29

Kubernetes jest obecnie standardem w zakresie orkiestracji kontenerów, oferującym zaawansowane funkcje zarządzania cyklem życia aplikacji. Platforma ta automatyzuje procesy wdrażania, skalowania i samonaprawy, opierając się na deklaratywnej konfiguracji określającej pożądany stan systemu. Mechanizm ReplicaSet zapewnia utrzymanie określonej liczby replik podów, a w przypadku awarii automatycznie uruchamia nowe instancje w miejsce tych, które uległy awarii.

Service mesh, realizowany przez narzędzia takie jak Istio czy Linkerd, rozszerza funkcjonalność orkiestracji o warstwę komunikacji między usługami. Przechwytuje on ruch sieciowy i dodaje funkcje takie jak wzajemne uwierzytelnianie TLS, równoważenie obciążenia, monitoring i tracing bez konieczności modyfikacji kodu aplikacji. Wdrożenie service mesh upraszcza zarządzanie bezpieczeństwem i obserwowalnością w dużych klastrach produkcyjnych.

Strategie wdrażania w Kubernetes obejmują rolling update, blue-green deployment i canary release, każda z nich oferuje inny kompromis między szybkością wdrożenia a ryzykiem. Rolling update stopniowo zastępuje stare pody nowymi, co zapewnia ciągłość działania usługi podczas aktualizacji. Canary release kieruje niewielki procent ruchu do nowej wersji, umożliwiając weryfikację stabilności przed pełnym wdrożeniem.

30/50
Modularność usług
  • Modularność to zasada projektowania systemów polegająca na dzieleniu ich na mniejsze, niezależne i wymienne moduły.
  • Każdy moduł ma jasno zdefiniowany interfejs i realizuje konkretną funkcję.
  • W architekturze usług, modularność jest realizowana poprzez dekompozycję systemu na autonomiczne usługi.
  • Taka struktura przynosi wiele korzyści: ułatwia zrozumienie i zarządzanie systemem, pozwala na niezależny rozwój i wdrażanie poszczególnych modułów, a także promuje reużywalność kodu.
  • Dobrze zaprojektowana modularna architektura jest bardziej elastyczna i łatwiejsza w utrzymaniu w długim okresie.
Ilustracja dla slajdu 30

Domain-Driven Design dostarcza metodyki projektowania modularnych systemów poprzez wyodrębnienie granic kontekstów, które odpowiadają poszczególnym domenom biznesowym. Każdy bounded context posiada własny język wszechobecny i własny model domeny, co zapobiega niekontrolowanemu rozrostowi odpowiedzialności pojedynczego modułu. Granice między kontekstami są ściśle określone i komunikacja odbywa się wyłącznie przez zdefiniowane interfejsy.

Hermetyzacja implementacji jest kluczową zasadą modularności, polegającą na ukrywaniu wewnętrznych szczegółów modułu za publicznym interfejsem. Dzięki temu zmiany wewnątrz modułu nie wpływają na innych konsumentów, co znacząco ułatwia ewolucję systemu. Wstrzykiwanie zależności jest wzorcem wspierającym modularność poprzez dostarczanie zależności z zewnątrz, co ułatwia testowanie i wymianę poszczególnych komponentów.

W architekturze mikroserwisowej modularność na poziomie kodu wspierana jest przez narzędzia do zarządzania monorepozytorium, takie jak Nx, Lerna czy Gradle z konfiguracją wielomodułową. Monorepozytorium umożliwia współdzielenie kodu między modułami przy zachowaniu niezależności wersjonowania i wdrażania. Analiza statyczna zależności między modułami, realizowana przez narzędzia takie jak ArchUnit w Javie, pozwala egzekwować reguły architektoniczne i zapobiegać powstawaniu niepożądanych cyklicznych zależności.

31/50
Konteneryzacja
Technologia ta, spopularyzowana przez narzędzie Docker, zrewolucjonizowała sposób budowania, wdrażania i uruchamiania aplikacji, stając się fundamentem nowoczesnych architektur mikroserwisowych i praktyk DevOps.
  • Konteneryzacja to metoda wirtualizacji na poziomie systemu operacyjnego, która pozwala na uruchamianie aplikacji i ich zależności w odizolowanych od siebie środowiskach zwanych kontenerami.
  • W przeciwieństwie do maszyn wirtualnych, kontenery nie zawierają całego systemu operacyjnego, a jedynie niezbędne biblioteki i pliki binarne, dzieląc jądro systemu operacyjnego z hostem.
  • Dzięki temu są niezwykle lekkie, szybkie i przenośne.
Ilustracja dla slajdu 31

Kontenery korzystają z mechanizmów jądra Linux, takich jak przestrzenie nazw (namespaces) i grupy kontrolne (cgroups), aby zapewnić izolację procesów i kontrolować dostęp do zasobów systemowych. Namespaces pozwalają na izolację widoku systemu plików, sieci, procesów i użytkowników, podczas gdy cgroups odpowiadają za limitowanie CPU, pamięci i przepustowości wejścia-wyjścia. To właśnie te mechanizmy, a nie wirtualizacja sprzętu, sprawiają, że kontenery są tak lekkie i szybkie w uruchamianiu.

Plik Dockerfile definiuje proces budowania obrazu kontenera, składający się z sekwencji warstw, które są pamiętane w systemie cache. Każda instrukcja w Dockerfile (FROM, RUN, COPY, CMD) tworzy nową warstwę, co pozwala na efektywne ponowne wykorzystanie już zbudowanych fragmentów podczas kolejnych kompilacji. Obrazy są przechowywane w rejestrach, takich jak Docker Hub lub prywatnych repozytoriach, skąd mogą być pobierane na dowolne środowisko.

W odróżnieniu od maszyn wirtualnych, które emulują cały sprzęt i zawierają własny system operacyjny, kontenery współdzielą jądro hosta, co eliminuje narzut związany z uruchamianiem osobnego systemu gościa. Dzięki temu na jednym hoście można uruchomić znacznie więcej kontenerów niż maszyn wirtualnych, a czas ich startu liczy się w milisekundach, a nie minutach. Konteneryzacja stała się standardem w nowoczesnych pipeline'ach CI/CD, ponieważ zapewnia spójność środowiska między developerem a produkcją.

32/50
Architektura Kubernetes
  • Kubernetes (często skracany do K8s) to otwarty system do automatyzacji wdrażania, skalowania i zarządzania skonteneryzowanymi aplikacjami.
  • Jego architektura opiera się na koncepcji klastra, składającego się z węzłów roboczych (worker nodes), które uruchamiają kontenery, oraz płaszczyzny sterowania (control plane), która zarządza całym klastrem.
  • Kluczowe komponenty płaszczyzny sterowania to serwer API, etcd (rozproszona baza klucz-wartość przechowująca stan klastra), scheduler (przypisujący pody do węzłów) i kontrolery. Kubernetes abstrahuje infrastrukturę, pozwalając deweloperom deklaratywnie opisywać pożądany stan aplikacji, a system sam dba o jego utrzymanie.
Miejsce na ilustracje

Płaszczyzna sterowania Kubernetes składa się z kilku kluczowych komponentów: serwer API (kube-apiserver) jest centralnym punktem komunikacji i udostępnia interfejs REST do zarządzania klastrem, etcd przechowuje cały stan klastra w postaci klucz-wartość, scheduler odpowiada za przypisywanie nowo utworzonych podów do odpowiednich węzłów, a controller manager uruchamia kontrolery dbające o utrzymanie pożądanego stanu. Każdy z tych komponentów może być replikowany w celu zapewnienia wysokiej dostępności płaszczyzny sterowania.

Na każdym węźle roboczym działają trzy podstawowe procesy: kubelet, który komunikuje się z serwerem API i zarządza kontenerami na węźle, kube-proxy odpowiadający za reguły sieciowe i przekierowywanie ruchu do podów, oraz środowisko uruchomieniowe kontenerów, takie jak Docker lub containerd. Węzły robocze są rejestrowane w klastrze i otrzymują zadania od schedulera, a kubelet na bieżąco raportuje ich stan do płaszczyzny sterowania.

Podstawowym obiektem w Kubernetes jest pod, czyli najmniejsza jednostka wdrożeniowa zawierająca jeden lub więcej kontenerów współdzielących przestrzeń sieciową i woluminy. Deployment zarządza replikacją podów i umożliwia deklaratywne aktualizacje z mechanizmem rollback, Service zapewnia stabilny punkt dostępu do grupy podów, a Ingress udostępnia aplikacje na zewnątrz klastra z obsługą routingu HTTPS i wirtualnych hostów. Kubernetes oferuje również zaawansowane mechanizmy, takie jak ConfigMap i Secret do zarządzania konfiguracją.

33/50
Chmura publiczna
  • Chmura publiczna to model dostarczania usług IT, w którym zasoby obliczeniowe, takie jak serwery, pamięć masowa czy bazy danych, są udostępniane przez zewnętrznego dostawcę (np. Amazon Web Services, Microsoft Azure, Google Cloud) za pośrednictwem publicznego Internetu.
  • Klienci płacą za faktyczne zużycie zasobów (model pay-as-you-go), co eliminuje potrzebę inwestowania w drogą, własną infrastrukturę.
  • Chmura publiczna oferuje ogromną skalowalność, elastyczność i dostęp do szerokiej gamy zaawansowanych usług, co pozwala firmom na szybkie wdrażanie innowacji i globalne skalowanie swoich aplikacji bez martwienia się o zarządzanie sprzętem.
Ilustracja dla slajdu 33

Trzej najwięksi dostawcy chmury publicznej – AWS, Microsoft Azure i Google Cloud – oferują infrastrukturę rozproszoną globalnie w postaci regionów i stref dostępności. Każdy region to odrębna geograficzna lokalizacja zawierająca co najmniej trzy strefy dostępności, które są odseparowane od siebie pod względem zasilania i sieci, ale połączone szybkimi łączami światłowodowymi o małym opóźnieniu. Taka architektura pozwala na budowanie systemów odpornych na awarie całych centrów danych.

Modele usług w chmurze publicznej dzielą się na trzy główne kategorie: IaaS (Infrastructure as a Service) udostępnia wirtualne maszyny, sieci i magazyny, dając największą kontrolę, PaaS (Platform as a Service) oferuje zarządzane platformy uruchomieniowe dla aplikacji, a SaaS (Software as a Service) dostarcza gotowe oprogramowanie dostępne przez przeglądarkę. Coraz większą popularność zyskuje model FaaS (Function as a Service), który pozwala uruchamiać pojedyncze funkcje w odpowiedzi na zdarzenia, co stanowi podstawę architektur serverless.

Bezpieczeństwo w chmurze publicznej opiera się na modelu współdzielonej odpowiedzialności (shared responsibility model), gdzie dostawca odpowiada za bezpieczeństwo infrastruktury, a klient za bezpieczeństwo w infrastrukturze, obejmujące konfigurację sieci, zarządzanie tożsamościami i szyfrowanie danych. Firmy korzystające z chmury publicznej muszą wdrożyć odpowiednie polityki IAM (Identity and Access Management), szyfrowanie danych w spoczynku i w tranzycie oraz regularne audyty bezpieczeństwa, aby spełnić wymogi regulacyjne.

34/50
Chmura hybrydowa
  • Architektura chmury hybrydowej łączy w sobie elementy chmury publicznej i chmury prywatnej (infrastruktury dedykowanej dla jednej organizacji).
  • Pozwala to firmom na elastyczne rozlokowanie swoich aplikacji i danych w zależności od wymagań dotyczących bezpieczeństwa, wydajności i kosztów.
  • Na przykład, krytyczne dane i aplikacje wymagające ścisłej kontroli mogą być przechowywane w chmurze prywatnej, podczas gdy mniej wrażliwe systemy lub aplikacje o zmiennym obciążeniu mogą być uruchamiane w chmurze publicznej.
  • Chmura hybrydowa oferuje "to, co najlepsze z obu światów", ale jej wdrożenie i zarządzanie jest bardziej złożone.
Ilustracja dla slajdu 34

Połączenie chmury publicznej z prywatną wymaga zastosowania dedykowanych rozwiązań sieciowych, takich jak VPN typu site-to-site, dedykowane łącza Direct Connect (AWS) lub ExpressRoute (Azure). Te technologie tworzą szyfrowany tunel lub dedykowane połączenie fizyczne między lokalnym centrum danych a chmurą publiczną, zapewniając stabilną i bezpieczną komunikację o przewidywalnych parametrach opóźnienia i przepustowości. Bez tych połączeń model hybrydowy traci swoją praktyczność.

Orkiestracja w środowisku hybrydowym wymaga narzędzi takich jak Kubernetes w wersji wieloklastrowej, które umożliwiają zarządzanie aplikacjami rozproszonymi między lokalną infrastrukturą a chmurą. Rozwiązania takie jak Anthos (Google), Azure Arc czy AWS Outposts pozwalają na spójne zarządzanie konfiguracją, politykami bezpieczeństwa i monitoringiem niezależnie od lokalizacji obciążenia. Dzięki temu organizacje mogą stopniowo migrować aplikacje do chmury publicznej bez konieczności przeprowadzania ryzykownej migracji typu "big bang".

Największym wyzwaniem chmury hybrydowej jest utrzymanie spójności operacyjnej i zarządzanie kosztami w rozproszonym środowisku. Każde środowisko (on-premises, public cloud) ma własne narzędzia do monitoringu, rozliczania i zarządzania, co wymaga wdrożenia ujednoliconej platformy obserwowalności i finansowej. Dodatkowo, aplikacje w środowisku hybrydowym muszą być projektowane z myślą o możliwych opóźnieniach sieciowych między lokalizacjami oraz o potencjalnych zakłóceniach w łączności między chmurą a lokalnym centrum danych.

35/50
IaC – wpływ na architekturę
  • Infrastruktura jako kod (IaC, Infrastructure as Code) to praktyka zarządzania i aprowizacji infrastruktury IT (serwerów, sieci, baz danych) za pomocą plików konfiguracyjnych, które są traktowane jak kod źródłowy oprogramowania.
  • Narzędzia takie jak Terraform czy Ansible pozwalają na deklaratywne opisywanie pożądanego stanu infrastruktury. IaC ma ogromny wpływ na architekturę, ponieważ umożliwia automatyzację, powtarzalność i wersjonowanie całego środowiska.
  • Dzięki temu można szybko i niezawodnie tworzyć identyczne środowiska deweloperskie, testowe i produkcyjne, co znacząco przyspiesza cykl wdrożeniowy i minimalizuje ryzyko błędów wynikających z ręcznej konfiguracji.
Ilustracja dla slajdu 35

Infrastruktura jako kod dzieli się na dwa główne podejścia: deklaratywne (Terraform, Pulumi, CloudFormation) i imperatywne (Ansible, Chef, Puppet). W podejściu deklaratywnym użytkownik opisuje pożądany stan końcowy infrastruktury, a narzędzie samo określa kroki niezbędne do osiągnięcia tego stanu, podczas gdy podejście imperatywne wymaga jawnego zdefiniowania sekwencji poleceń. Podejście deklaratywne jest obecnie preferowane ze względu na łatwość utrzymania i przewidywalność rezultatów.

Integracja IaC z systemami kontroli wersji, takimi jak Git, umożliwia stosowanie praktyk GitOps, gdzie repozytorium kodu jest jedynym źródłem prawdy o stanie infrastruktury. Każda zmiana konfiguracji wymaga pull requesta i przechodzi przez proces code review, co znacząco podnosi jakość i bezpieczeństwo zmian. Po zatwierdzeniu, pipeline CI/CD automatycznie stosuje zmiany w docelowym środowisku, zapewniając powtarzalność i eliminując błędy ręcznej konfiguracji.

Modułowość w IaC pozwala na tworzenie wielokrotnego użytku komponentów infrastrukturalnych, które można parametryzować i wersjonować niezależnie. Na przykład moduł Terraform definiujący klastry Kubernetes może być używany przez różne zespoły z różnymi parametrami, co zapewnia spójność konfiguracji i przyspiesza wdrażanie nowych projektów. IaC wymusza również na architektach myślenie o infrastrukturze w kategoriach kodu, co prowadzi do lepiej udokumentowanych i bardziej przemyślanych decyzji architektonicznych.

36/50
Schematy komunikacji
  • W architekturach rozproszonych istnieje wiele schematów (wzorców) komunikacji między usługami.
  • Do najprostszych należy bezpośrednia komunikacja klient-usługa, często realizowana synchronicznie przez REST API.
  • Bardziej zaawansowane wzorce obejmują API Gateway, który działa jako pojedynczy punkt wejścia do systemu, oraz komunikację opartą na zdarzeniach (event-driven), gdzie usługi reagują na zdarzenia publikowane w systemie kolejkowym.
  • Wybór odpowiedniego schematu zależy od specyfiki systemu.
  • Na przykład, wzorzec "publish-subscribe" doskonale nadaje się do systemów, gdzie wiele usług musi być powiadamianych o tym samym zdarzeniu.
Miejsce na ilustracje

Wzorzec publish-subscribe (pub-sub) opiera się na asynchronicznym przesyłaniu wiadomości za pośrednictwem brokera, takiego jak Apache Kafka lub RabbitMQ. Producent publikuje wiadomości na określony temat, a konsumenci subskrybują interesujące ich tematy, otrzymując wiadomości bez potrzeby bezpośredniej znajomości producenta. Taka dekorelacja umożliwia niezależne skalowanie konsumentów i dodawanie nowych bez modyfikacji istniejącego systemu, co jest kluczowe w architekturze mikroserwisów.

Komunikacja synchroniczna, najczęściej realizowana za pomocą REST API, jest intuicyjna i łatwa w debugowaniu, ale wprowadza silne sprzężenie czasowe między usługami. Problemy takie jak kaskadowe blokowanie zasobów czy trudności w obsłudze chwilowych niedostępności usług sprawiły, że wiele zespołów zaczęło stosować komunikację asynchroniczną dla operacji długotrwałych i krytycznych. Kolejki komunikatów buforują żądania i umożliwiają ich ponowne przetworzenie w przypadku błędu, co znacząco zwiększa niezawodność systemu.

Nowoczesne protokoły, takie jak gRPC oparty na HTTP/2 i protobufach, oferują znacznie lepszą wydajność niż REST/JSON dzięki binarnemu formatowi danych, strumieniowaniu i możliwości multiplexowania żądań. WebSocket umożliwia utrzymanie trwałego połączenia dwukierunkowego, co jest niezbędne w aplikacjach czasu rzeczywistego. GraphQL z kolei pozwala klientom precyzyjnie określać strukturę odpowiedzi, eliminując problem nadmiarowego lub niedostatecznego pobierania danych.

37/50
Limity i throttling
  • Wprowadzanie limitów (rate limiting) i kontrolowanie przepływu (throttling) to techniki stosowane w celu ochrony usług przed przeciążeniem i nadużyciami.
  • Rate limiting polega na ograniczeniu liczby żądań, które dany klient może wysłać do API w określonym przedziale czasowym (np. 100 żądań na minutę).
  • Throttling to bardziej zaawansowana forma kontroli, która może spowalniać obsługę żądań po przekroczeniu pewnego progu, zamiast je od razu odrzucać.
  • Implementacja tych mechanizmów jest kluczowa dla zapewnienia stabilności i sprawiedliwego dostępu do zasobów w publicznie dostępnych API, a także chroni przed atakami typu Denial of Service.
Ilustracja dla slajdu 37

Najczęściej stosowane algorytmy rate limitingu to token bucket i leaky bucket. Token bucket polega na dodawaniu tokenów do wiadra w stałym tempie – każde żądanie zużywa jeden token, a gdy wiadro jest puste, żądania są odrzucane, co pozwala na radzenie sobie z krótkotrwałymi skokami ruchu. Leaky bucket działa jak wyciekające wiadro, gdzie woda (żądania) wpływają ze zmienną szybkością, ale wypływają ze stałą, co zapewnia bardziej wyrównany przepływ. Wybór algorytmu zależy od charakterystyki obciążenia i wymagań biznesowych.

Implementacja rate limitingu najczęściej odbywa się na poziomie API Gateway (Kong, AWS API Gateway, Zuul) lub reverse proxy (Nginx, HAProxy). Rozwiązania te mogą przechowywać liczniki żądań w pamięci podręcznej, takiej jak Redis, co pozwala na współdzielenie stanu między wieloma instancjami bramy. W Kubernetes popularne jest stosowanie limitów na poziomie Ingress Controller, które mogą być dynamicznie konfigurowane w zależności od obciążenia poszczególnych usług.

W przypadku przekroczenia limitu serwer zwraca kod statusu HTTP 429 (Too Many Requests) wraz z nagłówkiem Retry-After wskazującym czas oczekiwania przed ponowną próbą. Klienci powinni implementować mechanizmy wycofywania wykładniczego (exponential backoff) z jitterem, aby uniknąć efektu thundering herd. Dobrze zaprojektowany system limitów i throttlingu powinien również uwzględniać priorytetyzację żądań, na przykład zapewniając wyższe limity dla klientów premium.

38/50
Wzorce projektowe
  • Wzorce projektowe to sprawdzone, powtarzalne rozwiązania typowych problemów napotykanych podczas projektowania oprogramowania.
  • W kontekście architektury usług sieciowych, istnieje wiele wzorców, które pomagają budować niezawodne i skalowalne systemy.
  • Przykłady to wzorzec "API Gateway", który centralizuje dostęp do mikroserwisów, "Circuit Breaker", który zwiększa odporność na awarie zależności, czy "Saga", który pomaga zarządzać transakcjami rozproszonymi.
  • Stosowanie wzorców projektowych pozwala czerpać z doświadczeń innych inżynierów, unikać typowych błędów i tworzyć bardziej dojrzałe i przemyślane architektury.
Ilustracja dla slajdu 38

Wzorce projektowe w kontekście usług sieciowych dzielą się na kategorie podobne do klasycznych wzorców z książki Gangu Czterech, ale zaadaptowane do potrzeb systemów rozproszonych. Wzorzec Sidecar polega na dołączeniu do głównego kontenera pomocniczego kontenera obsługującego zadania infrastrukturalne, takie jak przekazywanie logów, proxy sieciowe czy monitorowanie. Ambassador z kolei działa jako zewnętrzny proxy reprezentujący usługę, ułatwiając testowanie i izolację.

Circuit Breaker to jeden z najważniejszych wzorców odporności w systemach rozproszonych, który monitoruje liczbę błędów w komunikacji z usługą zależną. Po przekroczeniu progu błędów wyłącznik otwiera obwód, natychmiast odrzucając żądania bez próby połączenia, co chroni usługę przed kaskadowym przeciążeniem. Po upływie określonego czasu próbne żądania są przepuszczane w celu sprawdzenia, czy usługa wróciła do stanu poprawnego (stan półotwarty), a jeśli tak, obwód jest zamykany.

Wzorzec Saga rozwiązuje problem zarządzania transakcjami rozproszonymi w architekturze mikroserwisów, gdzie tradycyjne transakcje ACID nie są możliwe. Saga dzieli długą transakcję na sekwencję lokalnych transakcji, z których każda ma kompensującą akcję cofającą. W przypadku niepowodzenia jednej z transakcji, uruchamiane są akcje kompensujące w odwrotnej kolejności, co przywraca system do spójnego stanu bez konieczności stosowania kosztownych blokad rozproszonych.

39/50
Anti-patterny
  • Antywzorce (anti-patterns) to powszechnie stosowane, lecz nieskuteczne lub wręcz szkodliwe rozwiązania problemów projektowych.
  • Są to "pułapki", w które często wpadają niedoświadczeni architekci.
  • W architekturach usług przykładem antywzorca jest "rozproszony monolit", gdzie mikroserwisy są tak ściśle ze sobą powiązane, że nie można ich wdrażać ani skalować niezależnie.
  • Innym przykładem jest "magiczna magistrala" (magical bus), gdzie cała logika biznesowa jest umieszczana w ESB, co czyni ją pojedynczym punktem awarii i wąskim gardłem.
  • Rozpoznawanie i unikanie antywzorców jest równie ważne, jak stosowanie dobrych wzorców projektowych.
Ilustracja dla slajdu 39

Rozproszony monolit (distributed monolith) to jeden z najgroźniejszych antywzorców w architekturze mikroserwisów. Powstaje, gdy mikroserwisy są od siebie silnie zależne poprzez bezpośrednie wywołania synchroniczne, współdzielone bazy danych lub ścisłe powiązania wersji. W efekcie system posiada wszystkie wady rozproszenia (złożoność sieciowa, opóźnienia) bez zalet mikroserwisów (niezależne wdrażanie, skalowanie), a wdrożenie nawet prostej zmiany wymaga koordynacji wielu zespołów.

Innym częstym antywzorcem jest nadmierna inżynieria (over-engineering), polegająca na implementacji zaawansowanych rozwiązań na długo przed pojawieniem się rzeczywistej potrzeby. Przykładem jest budowanie rozbudowanej architektury zdarzeniowej z Apache Kafka i setkami mikroserwisów dla prostej aplikacji obsługującej kilkuset użytkowników. Takie podejście niepotrzebnie zwiększa złożoność operacyjną i koszty utrzymania, jednocześnie spowalniając rozwój w początkowych fazach projektu.

Antywzorzec pojedynczego punktu awarii (single point of failure) w kontekście usług przejawia się najczęściej w scentralizowanych komponentach takich jak wspólna baza danych dla wszystkich mikroserwisów, jedna magistrala ESB czy pojedyncza brama API bez redundancji. Kolejnym powszechnym błędem jest brak odpowiedniego monitoringu i alertowania, który powoduje, że architekt odkrywa problemy dopiero w momencie, gdy użytkownicy zgłaszają awarię, zamiast być o nich informowanym proaktywnie.

40/50
Tworzenie diagramów
Diagramy architektoniczne są kluczowym narzędziem komunikacji, pozwalającym na wizualizację i zrozumienie złożonych systemów.
  • Istnieje wiele standardów i notacji do tworzenia takich diagramów.
  • Jednym z popularnych jest model C4 (Context, Containers, Components, Code), który pozwala opisywać architekturę na różnych poziomach abstrakcji, od ogólnego kontekstu biznesowego po szczegóły implementacyjne.
  • Inne standardy to UML (Unified Modeling Language) czy ArchiMate.
  • Niezależnie od wybranej notacji, dobre diagramy powinny być czytelne, spójne i jednoznacznie przedstawiać strukturę oraz relacje między komponentami systemu.
Miejsce na ilustracje

Model C4 (Context, Containers, Components, Code) został zaproponowany przez Simona Browna jako sposób na stopniowe zagłębianie się w szczegóły architektury. Poziom Context pokazuje system jako czarną skrzynkę na tle otoczenia i relacji z użytkownikami oraz systemami zewnętrznymi. Poziom Containers (nie mylić z Dockerem) ukazuje główne bloki aplikacji – serwery WWW, bazy danych, kolejki – i interakcje między nimi. Poziom Component rozkłada każdy kontener na moduły, a poziom Code może przedstawiać wybrane elementy w formie diagramów klas lub innych szczegółowych notacji.

Do tworzenia diagramów architektonicznych dostępne są zarówno narzędzia graficzne, takie jak Draw.io, Lucidchart i Microsoft Visio, jak i narzędzia tekstowe, takie jak PlantUML i Mermaid. Narzędzia tekstowe zyskują coraz większą popularność, ponieważ diagramy generowane z kodu można przechowywać w repozytorium Git i wersjonować razem z kodem aplikacji. Mermaid jest szczególnie dobrze zintegrowany z platformami takimi jak GitHub, GitLab i Notion, umożliwiając renderowanie diagramów bezpośrednio w dokumentacji.

Przy tworzeniu diagramów należy przestrzegać kilku podstawowych zasad: ograniczać liczbę elementów na jednym diagramie do maksymalnie 15–20, stosować spójną notację i kolorystykę w całej dokumentacji oraz opatrywać każdy diagram opisem i datą aktualizacji. Diagram powinien przedstawiać jeden konkretny aspekt architektury, a nie próbować pokazać wszystkiego naraz. Warto również stosować etykiety na liniach komunikacji, określające protokół i typ interakcji (synchroniczna, asynchroniczna), aby diagram był jak najbardziej jednoznaczny.

41/50
Dokumentacja architektury
  • Dokumentacja architektury to zbiór artefaktów, które opisują projekt, strukturę i kluczowe decyzje podjęte podczas tworzenia systemu.
  • Jest ona niezbędna do utrzymania i rozwoju systemu w długim okresie, zwłaszcza w zmieniających się zespołach.
  • Dobra dokumentacja powinna zawierać nie tylko diagramy, ale także opisy komponentów, ich odpowiedzialności, interfejsów oraz, co bardzo ważne, uzasadnienie podjętych decyzji architektonicznych (tzw. Architecture Decision Records - ADRs).
  • Utrzymywanie aktualnej i przystępnej dokumentacji jest jednym z największych wyzwań, ale inwestycja ta zwraca się wielokrotnie w przyszłości.
Ilustracja dla slajdu 41

Architecture Decision Records (ADR) to jeden z najskuteczniejszych mechanizmów dokumentowania kluczowych decyzji architektonicznych podejmowanych w trakcie cyklu życia projektu. Każdy ADR opisuje konkretny problem, rozważane opcje, podjętą decyzję oraz jej uzasadnienie wraz z konsekwencjami technicznymi i biznesowymi. Dzięki temu nowi członkowie zespołu mogą szybko zrozumieć, dlaczego system został zaprojektowany w określony sposób, bez konieczności odtwarzania całego kontekstu historycznego.

Model C4 stanowi praktyczne podejście do wizualizacji architektury oprogramowania w czterech poziomach abstrakcji: kontekst, kontenery, komponenty i kod. Każdy poziom dostarcza innej perspektywy, co pozwala dostosować szczegółowość dokumentacji do odbiorcy – od interesariuszy biznesowych po programistów. Narzędziem wspierającym to podejście jest Structurizr, które umożliwia tworzenie diagramów w sposób tekstowy i utrzymywanie ich w repozytorium kodu.

Automatyzacja generowania dokumentacji z kodu źródłowego to kolejny krok w kierunku zapewnienia jej aktualności. Techniki takie jak literate programming czy narzędzia typu AsciiDoc pozwalają łączyć opisy architektoniczne bezpośrednio z implementacją. W praktyce oznacza to, że każda zmiana w kodzie może automatycznie aktualizować odpowiadające jej fragmenty dokumentacji, co eliminuje problem rozbieżności między teorią a rzeczywistością systemu produkcyjnego.

42/50
Przykłady produkcyjne
  • Analiza rzeczywistych, produkcyjnych architektur wielkich firm technologicznych, takich jak Netflix, Amazon czy Google, dostarcza bezcennych lekcji.
  • Netflix, pionier architektury mikroserwisów, pokazał, jak budować globalnie skalowalny i odporny na awarie system streamingowy.
  • Amazon z kolei udowodnił skuteczność architektury zorientowanej na usługi w kontekście gigantycznej platformy e-commerce.
  • Uczenie się na ich sukcesach i porażkach, zrozumienie, jakie problemy rozwiązywali i jakich kompromisów dokonywali, jest jedną z najlepszych metod nauki dla każdego architekta systemów.
Ilustracja dla slajdu 42

Netflix zbudował jedną z najbardziej zaawansowanych architektur mikroserwisowych na świecie, obsługującą setki milionów użytkowników w ponad 190 krajach. Kluczowym elementem ich stosu technologicznego jest platforma Eureka odpowiedzialna za rejestrację i odnajdywanie usług, co umożliwia dynamiczne skalowanie bez ingerencji operatora. Każdy mikroserwis w środowisku Netflix jest niezależnie wdrażany i może być skalowany w zależności od bieżącego obciążenia, co zapewnia optymalne wykorzystanie zasobów.

Amazon od wczesnych lat dwutysięcznych stosuje architekturę zorientowaną na usługi (SOA), która przekształciła się w jedną z największych platform e-commerce na globie. Każdy zespół w Amazon odpowiada za maksymalnie kilkanaście osób i zarządza własnymi usługami, które komunikują się przez dobrze zdefiniowane interfejsy API. Ta struktura organizacyjna, znana jako zasada dwupizzowego zespołu, pozwala na decentralizację decyzji i szybkie iterowanie nad poszczególnymi komponentami platformy.

Google z kolei opracowało system Borg, a następnie jego następcę Kubernetes, który zrewolucjonizował sposób zarządzania konteneryzowanymi aplikacjami na ogromną skalę. Google wewnętrznie obsługuje miliardy zapytań dziennie przy użyciu globalnie rozproszonych klastrów obliczeniowych, które automatycznie zarządzają alokacją zasobów i odzyskiwaniem po awarii. Doświadczenia Google pokazują, że niezawodność na poziomie pięciu dziewiątek wymaga nie tylko doskonałego oprogramowania, ale również przemyślanej architektury sieci i systemów przechowywania danych.

43/50
Studium przypadku
  • Przeanalizujmy studium przypadku: transformację monolitycznej aplikacji e-commerce do architektury mikroserwisów.
  • Początkowo, aplikacja była trudna do skalowania w okresach promocyjnych, a wdrożenie każdej małej zmiany wymagało testowania i wdrażania całego systemu.
  • Po dekompozycji na usługi takie jak "katalog produktów", "koszyk", "płatności" i "zamówienia", każda z nich mogła być skalowana niezależnie.
  • Usługa "katalog produktów" wymagała dużej mocy do obsługi ruchu, podczas gdy usługa "płatności" musiała być przede wszystkim bezpieczna i niezawodna.
  • Ta transformacja, choć kosztowna, pozwoliła na zwiększenie elastyczności biznesowej i niezawodności platformy.
Ilustracja dla slajdu 43

Wzorzec Strangler Fig (duszący figowiec) jest najczęściej rekomendowaną strategią migracji z monolitu do mikroserwisów, ponieważ pozwala na stopniowe zastępowanie fragmentów starego systemu nowymi usługami bez przestojów. Polega on na dodawaniu warstwy pośredniej, która przechwytuje żądania i kieruje je do nowej lub starej implementacji w zależności od stopnia migracji poszczególnych funkcjonalności. Dzięki temu ryzyko związane z transformacją jest minimalizowane, a zespół może dostarczać wartość biznesową w krótkich iteracjach.

Jednym z największych wyzwań przy dekompozycji bazy danych jest wydzielenie wspólnego schematu na osobne, niezależne magazyny danych dla każdej usługi. W przypadku aplikacji e-commerce kluczowe jest zapewnienie spójności transakcyjnej między usługami koszyka, płatności i zamówień, co prowadzi do zastosowania wzorca Saga. Saga dzieli długotrwałą transakcję na serię lokalnych transakcji z mechanizmami kompensacyjnymi, które cofają zmiany w przypadku niepowodzenia któregoś z kroków.

Wzorzec API Gateway okazuje się niezbędny w architekturze mikroserwisów, ponieważ zapewnia pojedynczy punkt wejścia dla klientów zewnętrznych i agreguje odpowiedzi z wielu usług backendowych. Brama API odpowiada również za uwierzytelnianie, autoryzację, ograniczanie szybkości żądań oraz transformację protokołów komunikacyjnych. W praktyce produkcyjnej wykorzystuje się do tego rozwiązania takie jak Kong, Zuul lub managed API Gateway oferowane przez dostawców chmurowych.

44/50
Najczęstsze błędy
  • Podczas projektowania architektur usług sieciowych istnieje kilka częstych błędów, których należy unikać.
  • Jednym z nich jest przedwczesna optymalizacja, czyli rozwiązywanie problemów ze skalowalnością, zanim jeszcze się pojawią, co niepotrzebnie komplikuje system.
  • Innym błędem jest ślepe podążanie za trendami, na przykład wdrażanie mikroserwisów w małym projekcie, gdzie prostszy monolit byłby lepszym rozwiązaniem.
  • Często popełnianym błędem jest również ignorowanie kwestii bezpieczeństwa i monitoringu na wczesnych etapach projektu, co prowadzi do poważnych problemów w środowisku produkcyjnym.
Miejsce na ilustracje

Jednym z najgroźniejszych zjawisk w świecie architektury oprogramowania jest tzw. rozproszony monolit (distributed monolith), który powstaje, gdy zespoły dzielą system na mikroserwisy, ale nie rozwiązują problemów związanych z komunikacją sieciową i współdzieleniem danych. W praktyce oznacza to, że usługi są silnie ze sobą sprzężone poprzez synchroniczne wywołania REST lub wspólne bazy danych, co prowadzi do wszystkich wad systemów rozproszonych bez żadnych korzyści wynikających z niezależności usług. Rozpoznanie tego antywzorca wymaga analizy metryk tak zwanych kolaboracji między serwisami oraz czasu odpowiedzi na zapytania międzyserwisowe.

Kolejnym częstym błędem jest nieprawidłowe wyznaczenie granic kontekstów odpowiedzialności przy projektowaniu mikroserwisów, co prowadzi do naruszenia zasady wysokiej spójności i niskiego sprzężenia. Usługi, które często zmieniają się z tych samych powodów biznesowych powinny znajdować się w jednym kontekście, podczas gdy te zmieniające się niezależnie powinny być rozdzielone. Pomocnym narzędziem w tym zakresie jest analiza Event Storming, która pozwala zespołowi wspólnie zdefiniować granice domen na podstawie zdarzeń biznesowych.

Niedocenianie opóźnień sieciowych i kosztów komunikacji międzyprocesowej to trzeci poważny problem pojawiający się w projektach studenckich i produkcyjnych. Programiści przyzwyczajeni do wywołań lokalnych często zakładają, że wywołania zdalne mają pomijalny koszt, co w skali systemu rozproszonego prowadzi do katastrofalnych spadków wydajności. Kluczowym rozwiązaniem jest stosowanie wzorców takich jak buforowanie odpowiedzi, komunikacja asynchroniczna z wykorzystaniem kolejek komunikatów oraz projektowanie interfejsów gruboziarnistych, które minimalizują liczbę niezbędnych wywołań sieciowych.

45/50
Wyzwania skalowania
Jednym z fundamentalnych jest utrzymanie spójności danych (data consistency) między wieloma węzłami i usługami.
  • Skalowanie systemów rozproszonych niesie ze sobą wiele wyzwań.
  • Twierdzenie CAP (Consistency, Availability, Partition tolerance) mówi, że w systemie rozproszonym można zagwarantować co najwyżej dwie z tych trzech cech.
  • Inne wyzwania to złożoność operacyjna związana z zarządzaniem setkami lub tysiącami serwerów, trudności w monitorowaniu i debugowaniu systemów, gdzie żądanie przechodzi przez wiele usług, oraz zapewnienie bezpieczeństwa w rozległej i dynamicznej infrastrukturze.
Ilustracja dla slajdu 45

Skalowanie horyzontalne, czyli dodawanie kolejnych instancji usług zamiast zwiększania mocy pojedynczej maszyny, jest podstawową strategią stosowaną w nowoczesnych systemach rozproszonych. Wymaga ono jednak zastosowania zaawansowanych mechanizmów równoważenia obciążenia, takich jak algorytmy round-robin, najmniejszej liczby połączeń czy spójnego haszowania (consistent hashing). Twierdzenie CAP przypomina, że w systemie rozproszonym nie można jednocześnie zapewnić pełnej spójności, dostępności i odporności na partycjonowanie sieci, co zmusza architektów do świadomych kompromisów.

Sharding baz danych jest jedną z najskuteczniejszych, ale i najbardziej złożonych technik skalowania warstwy trwałego przechowywania danych. Polega on na podziale dużej bazy danych na mniejsze, niezależne fragmenty zwane shardami, które są rozproszone na wielu serwerach. Kluczowym wyzwaniem jest wybór odpowiedniego klucza shardowania – jeśli klucz zostanie źle dobrany, dane rozłożą się nierównomiernie, co zniweluje korzyści ze skalowania i może prowadzić do przeciążenia pojedynczych węzłów.

Strategie buforowania danych na różnych poziomach architektury stanowią fundament wydajnych systemów na masową skalę. Na poziomie przeglądarki stosuje się nagłówki Cache-Control i ETag, na poziomie sieci dostarczania treści wykorzystuje się CDN, a na poziomie aplikacji rozwiązania takie jak Redis czy Memcached. Każda z tych warstw ma inne charakterystyki opóźnień i spójności danych, co wymaga starannego zaprojektowania polityki unieważniania pamięci podręcznej, aby użytkownicy zawsze otrzymywali aktualne dane bez zbędnych opóźnień.

46/50
Analiza ryzyka
  • Analiza ryzyka jest nieodłącznym elementem procesu projektowania architektury.
  • Polega ona na identyfikacji potencjalnych zagrożeń dla systemu, ocenie prawdopodobieństwa ich wystąpienia oraz ich potencjalnego wpływu na biznes.
  • Ryzyka mogą być techniczne (np. awaria bazy danych, błąd w oprogramowaniu), operacyjne (np. błąd ludzki podczas wdrożenia) lub związane z bezpieczeństwem (np. atak hakerski).
  • Po zidentyfikowaniu ryzyk, architekt musi podjąć decyzje o ich mitygacji, na przykład poprzez wdrożenie redundancji, planów odzyskiwania po awarii (Disaster Recovery) czy dodatkowych zabezpieczeń.
Ilustracja dla slajdu 46

Macierz ryzyka, znana również jako heat map, jest praktycznym narzędziem wizualizującym zidentyfikowane zagrożenia w układzie prawdopodobieństwa i wpływu biznesowego. Ryzyka znajdujące się w polu wysokiego prawdopodobieństwa i wysokiego wpływu wymagają natychmiastowych działań mitygacyjnych, podczas gdy te o niskim wpływie można zaakceptować lub monitorować. Proces ten powinien być cyklicznie powtarzany w ramach przeglądów architektonicznych, ponieważ profil ryzyka systemu zmienia się wraz z jego ewolucją i pojawianiem się nowych zagrożeń.

Metryki MTTR (Mean Time To Recover) i MTBF (Mean Time Between Failures) są kluczowymi wskaźnikami używanymi do pomiaru niezawodności systemów produkcyjnych. MTBF określa średni czas poprawnego działania między kolejnymi awariami, podczas gdy MTTR mierzy średni czas przywrócenia pełnej funkcjonalności po wystąpieniu incydentu. Dążenie do maksymalizacji MTBF i minimalizacji MTTR stanowi jeden z głównych celów każdej strategii architektonicznej ukierunkowanej na wysoką dostępność i niezawodność usług.

Plan odzyskiwania po awarii (Disaster Recovery) powinien obejmować nie tylko techniczne aspekty przywracania systemu, ale również procedury komunikacji z interesariuszami i użytkownikami. Kluczowymi elementami są tutaj cele RPO (Recovery Point Objective) określające dopuszczalną utratę danych oraz RTO (Recovery Time Objective) definiujące maksymalny akceptowalny czas przestoju. Regularne testy DR, przeprowadzane w kontrolowanych warunkach, pozwalają zweryfikować skuteczność przyjętych założeń i przeszkolić zespół operacyjny w zakresie reagowania na awarie.

47/50
Narzędzia architekta
  • Współczesny architekt systemów ma do dyspozycji szeroką gamę narzędzi, które wspierają jego pracę.
  • Do modelowania i tworzenia diagramów służą aplikacje takie jak Draw.io, Lucidchart czy specjalistyczne narzędzia wspierające notację ArchiMate.
  • Narzędzia do Infrastructure as Code, takie jak Terraform, pozwalają na definiowanie i zarządzanie infrastrukturą.
  • Platformy chmurowe (AWS, Azure, GCP) oferują gotowe, zarządzane usługi, które przyspieszają budowę systemów.
  • Niezbędne są również narzędzia do monitoringu i obserwowalności, takie jak Prometheus, Grafana czy systemy APM (Application Performance Monitoring), które dają wgląd w działanie systemu produkcyjnego.
Ilustracja dla slajdu 47

Notacja ArchiMate stanowi standard modelowania architektury korporacyjnej, który umożliwia opisanie warstw biznesowych, aplikacyjnych i technologicznych w spójny, formalny sposób. W przeciwieństwie do UML, który koncentruje się głównie na szczegółach implementacyjnych oprogramowania, ArchiMate dostarcza perspektywy strategicznej łączącej cele biznesowe z rozwiązaniami technicznymi. Narzędzia takie jak Archi czy Sparx Enterprise Architect implementują tę notację, umożliwiając tworzenie kompleksowych modeli architektonicznych całego przedsiębiorstwa.

Infrastructure as Code ewoluował od prostych skryptów konfiguracyjnych do zaawansowanych języków deklaratywnych, które w pełni definiują infrastrukturę w repozytorium kodu. Terraform, wykorzystujący własny język HCL, umożliwia zarządzanie infrastrukturą w modelu wielochmurowym, podczas gdy Pulumi pozwala na definiowanie zasobów w popularnych językach programowania takich jak TypeScript czy Python. Obie te technologie wspierają mechanizmy dryftu konfiguracji, które automatycznie wykrywają rozbieżności między stanem deklarowanym a rzeczywistym środowiska.

Narzędzia Application Performance Monitoring (APM), takie jak Datadog, New Relic czy Dynatrace, dostarczają kompleksowego wglądu w działanie systemów produkcyjnych na wielu poziomach abstrakcji. Umożliwiają one śledzenie pojedynczych żądań przechodzących przez dziesiątki mikroserwisów, identyfikację wąskich gardeł wydajnościowych oraz wykrywanie anomalii w czasie rzeczywistym. Integracja APM z narzędziami do zarządzania incydentami, takimi jak PagerDuty czy Opsgenie, pozwala na automatyczne eskalowanie alertów i skrócenie czasu reakcji na problemy produkcyjne.

48/50
Metody walidacji
Walidacja architektury to proces weryfikacji, czy zaprojektowany system spełnia postawione przed nim wymagania niefunkcjonalne, takie jak wydajność, bezpieczeństwo czy niezawodność.
  • Istnieje wiele metod walidacji.
  • Przeglądy architektoniczne (Architecture Reviews) polegają na ocenie projektu przez grupę ekspertów.
  • Tworzenie prototypów i dowodów koncepcji (Proof of Concept) pozwala na wczesne przetestowanie ryzykownych założeń.
  • Testy obciążeniowe i wydajnościowe weryfikują, jak system zachowuje się pod dużym obciążeniem.
  • Coraz popularniejszą metodą jest również inżynieria chaosu (Chaos Engineering), polegająca na kontrolowanym wprowadzaniu awarii do systemu produkcyjnego w celu sprawdzenia jego odporności.
Miejsce na ilustracje

Metoda ATAM (Architecture Tradeoff Analysis Method) opracowana przez Software Engineering Institute w Carnegie Mellon jest ustrukturyzowanym podejściem do ewaluacji decyzji architektonicznych w kontekście wymagań niefunkcjonalnych. Proces ATAM polega na identyfikacji scenariuszy użytkowania, które reprezentują kluczowe atrybuty jakościowe systemu, a następnie analizie, w jaki sposób proponowana architektura wpływa na te scenariusze. Wynikiem jest mapa punktów wrażliwych i kompromisów, która pozwala zainteresowanym stronom podejmować świadome decyzje przed przystąpieniem do implementacji.

Testy obciążeniowe z użyciem narzędzi takich jak Apache JMeter, Gatling czy k6 pozwalają na symulowanie rzeczywistych wzorców ruchu i identyfikację wąskich gardeł wydajnościowych na długo przed wdrożeniem produkcyjnym. Kluczowe jest projektowanie scenariuszy testowych odzwierciedlających rzeczywiste zachowania użytkowników, włączając w to profile szczytowego obciążenia, ataki DDoS oraz nietypowe sekwencje operacji. Wyniki tych testów powinny bezpośrednio przekładać się na decyzje o skalowaniu zasobów, optymalizacji zapytań bazodanowych czy wprowadzeniu mechanizmów buforowania.

Architecture Fitness Functions, koncepcja spopularyzowana przez Neala Forda, to mechanizm automatycznej walidacji cech architektonicznych poprzez zdefiniowanie zestawu testów weryfikujących kluczowe właściwości systemu. Przykładem może być test sprawdzający, czy żaden mikroserwis nie wykonuje bezpośredniego zapytania do bazy danych innego serwisu, lub czy czas odpowiedzi na żądania API nie przekracza zdefiniowanego progu. Takie funkcje sprawnościowe mogą być wykonywane w ramach potoku CI/CD, zapewniając ciągłą zgodność z założeniami architektonicznymi przez cały cykl życia systemu.

49/50
Podsumowanie
  • Architektura usług sieciowych to złożona dziedzina, która ewoluowała od prostych modeli klient-serwer do skomplikowanych, globalnie rozproszonych systemów opartych na mikroserwisach i chmurze.
  • Kluczowe koncepcje, które poznaliśmy, to modele architektoniczne, takie jak monolit i mikroserwisy, zasady budowania systemów o wysokiej dostępności i skalowalności, a także znaczenie bezpieczeństwa, monitoringu i automatyzacji.
  • Dobra architektura nie jest celem samym w sobie, ale narzędziem, które pozwala realizować cele biznesowe, zapewniając, że budowane systemy są niezawodne, wydajne i możliwe do utrzymania w długim okresie.
Ilustracja dla slajdu 49

Architektura usług sieciowych wymaga od projektanta umiejętności balansowania między teorią akademicką a praktycznymi ograniczeniami środowiska produkcyjnego. Każda decyzja architektoniczna pociąga za sobą konsekwencje w postaci kompromisów między wydajnością, bezpieczeństwem, kosztami utrzymania i złożonością operacyjną. Kluczową kompetencją architekta jest zdolność przewidywania długoterminowych skutków wyborów technologicznych oraz umiejętność adaptacji architektury do zmieniających się warunków biznesowych i technicznych.

Automatyzacja procesów wdrożeniowych, monitoringu i skalowania stanowi fundament nowoczesnych architektur chmurowych i mikroserwisowych. Narzędzia CI/CD, takie jak Jenkins, GitLab CI czy GitHub Actions, umożliwiają bezpieczne i powtarzalne wdrażanie zmian w środowiskach produkcyjnych z minimalnym udziałem człowieka. W połączeniu z praktykami GitOps i deklaratywnym zarządzaniem infrastrukturą, automatyzacja pozwala osiągnąć poziom niezawodności i szybkości dostarczania oprogramowania niedostępny w tradycyjnych modelach operacyjnych.

Ciągłe uczenie się i śledzenie trendów technologicznych jest nieodzowną częścią pracy architekta, ale wymaga krytycznego podejścia do nowości. Nie każde nowe narzędzie czy wzorzec projektowy jest odpowiedni dla każdego problemu, a umiejętność odrzucenia modnego rozwiązania na rzecz prostszego, sprawdzonego podejścia jest często cenniejsza niż znajomość najnowszych frameworków. Wartościowym źródłem wiedzy są raporty branżowe, analizy przypadku i konferencje techniczne, ale najwięcej uczymy się poprzez praktyczne eksperymentowanie i analizę własnych błędów architektonicznych.

50/50
Wnioski końcowe
Pamiętajmy, że architektura to proces ciągły – systemy żyją, ewoluują i muszą być regularnie oceniane i dostosowywane do zmieniającej się rzeczywistości.
  • Projektowanie architektury to sztuka kompromisów.
  • Nie ma jednego, uniwersalnego rozwiązania, które byłoby najlepsze w każdej sytuacji.
  • Wybór między monolitem a mikroserwisami, skalowaniem pionowym a poziomym, czy chmurą publiczną a prywatną zawsze zależy od specyficznego kontekstu, wymagań biznesowych, budżetu i kompetencji zespołu.
  • Najważniejszą cechą dobrego architekta jest umiejętność analizy tych kompromisów i podejmowania świadomych decyzji.
Ilustracja dla slajdu 50

Projektowanie architektury oprogramowania to proces iteracyjny, w którym każda decyzja powinna być podejmowana w kontekście konkretnych wymagań biznesowych, ograniczeń budżetowych i kompetencji zespołu programistycznego. Nie istnieje uniwersalne rozwiązanie pasujące do wszystkich problemów, a próba zastosowania mikroserwisów w każdym projekcie jest równie błędna jak kurczowe trzymanie się monolitu w sytuacji wymagającej rozproszenia. Świadomość własnych ograniczeń i gotowość do weryfikacji przyjętych założeń poprzez prototypowanie i testy to cechy wyróżniające dojrzałych architektów.

Bezpieczeństwo nie może być traktowane jako warstwa dodawana na końcu procesu projektowego, ale powinno przenikać wszystkie poziomy architektury od pierwszego dnia. Stosowanie zasad Security by Design, regularne przeprowadzanie audytów bezpieczeństwa oraz implementacja mechanizmów zero-trust w komunikacji między serwisami to standardy obowiązujące w nowoczesnych systemach produkcyjnych. Zaniedbanie tych aspektów prowadzi nie tylko do ryzyka wycieku danych, ale także do utraty zaufania klientów i konsekwencji prawnych wynikających z regulacji takich jak RODO.

Kończąc ten wykład, zachęcamy do dalszego zgłębiania przedstawionych zagadnień poprzez samodzielne projekty, analizę open sourceowych systemów i uczestnictwo w społecznościach architektonicznych. Wiedza zdobyta podczas kursu stanowi solidne fundamenty, ale prawdziwe mistrzostwo w projektowaniu architektur przychodzi z doświadczeniem i refleksją nad własnymi projektami. Pamiętajcie, że najlepsza architektura to taka, która spełnia swoje zadanie będąc jednocześnie na tyle prosta, aby każdy członek zespołu mógł ją zrozumieć i efektywnie rozwijać.