poniedziałek, 28 kwietnia 2025

Wzorce MSA - Testy kontraktowe i Consumer-Driven Contract

 W architekturze mikroserwisów komunikacja między usługami to chleb powszedni. Serwis A woła B, B woła C, a C czasem pyta jeszcze o coś D... I wszystko działa — do pierwszej zmiany w jednym z tych komponentów.



Zamiast "to się chyba uda", lepiej postawić na testy kontraktowe, a konkretnie na podejście Consumer-Driven Contract (CDC). To technika, która pozwala upewnić się, że komunikujące się systemy rozumieją się bez niedomówień.

Czym jest test kontraktowy?

To umowa między usługą kliencką (consumer) a usługą dostarczającą dane (provider).
Kontrakt opisuje co dokładnie oczekuje klient, a testy sprawdzają, czy dostawca spełnia te wymagania.

Nie chodzi tu o pełne testy integracyjne – tylko o weryfikację zachowania w określonych scenariuszach komunikacyjnych.

Consumer-Driven Contract (CDC) – o co chodzi?

W podejściu CDC:

  1. Klient definiuje kontrakt – np. jaką strukturę odpowiedzi HTTP oczekuje po zapytaniu GET /orders/{id}.

  2. Kontrakt jest zapisywany jako artefakt (np. plik .json, .yml, .pact).

  3. Dostawca (provider) implementuje API, które musi spełniać oczekiwania konsumentów.

  4. W CI/CD kontrakt jest weryfikowany po stronie providera, by uniknąć regresji.

Przykład (Pact)

Załóżmy, że aplikacja frontendowa (consumer) oczekuje takiej odpowiedzi z serwisu zamówień:


{ "id": "abc123", "status": "DELIVERED", "total": 99.99 }

Konsument definiuje kontrakt:


const provider = new Pact({ consumer: 'Frontend', provider: 'OrderService', }); provider .given('Order exists') .uponReceiving('a request for order details') .withRequest({ method: 'GET', path: '/orders/abc123', }) .willRespondWith({ status: 200, body: { id: like('abc123'), status: like('DELIVERED'), total: like(99.99), }, });

📤 Kontrakt jest publikowany do brokera (np. Pactflow), a provider w swoim CI odpala testy, które go weryfikują.

Dlaczego warto?

  • Zwiększasz pewność, że nie złamiesz kompatybilności

  • Wcześnie wykrywasz konflikty między zespołami

  • Odchudzasz testy E2E (bo nie musisz testować wszystkiego z wszystkimi)

  • Automatyzujesz weryfikację zgodności API

  • Ułatwiasz niezależne wdrażanie mikroserwisów

Typowe problemy bez CDC

  • 🔴 Zmiana API w jednym mikroserwisie powoduje błędy w innym (niespójność)

  • 🔴 Brak testów integracyjnych → produkcja testuje za nas

  • 🔴 "Ale przecież dokumentacja mówiła inaczej..."

  • 🔴 Trudność w wersjonowaniu i utrzymaniu API

Popularne narzędzia CDC

NarzędzieJęzyk / EkosystemUwagi
PactJava, JS, Python, .NETNajpopularniejsze narzędzie CDC
Spring Cloud ContractJavaIntegracja z Spring Boot
HoverflyHTTP proxy, języki dowolneSimulacja usług w testach
ContractTestJS, RESTLekkie testy kontraktowe

Jak wdrożyć CDC w praktyce

  1. Zdefiniuj kontrakt po stronie klienta (frontend, inny mikroserwis)

  2. Publikuj kontrakt do repozytorium (broker)

  3. W CI providera uruchamiaj testy weryfikujące kontrakt

  4. Nie wdrażaj providerów, którzy łamią oczekiwania klientów

  5. Automatyzuj ten proces w CI/CD

Podsumowanie

Testy kontraktowe i podejście CDC to must-have w ekosystemie mikroserwisów:

🔹 Gwarantują kompatybilność usług
🔹 Ułatwiają komunikację między zespołami
🔹 Wzmacniają kulturę DevOps i ciągłą integrację
🔹 Ograniczają chaos wersjonowania API

poniedziałek, 21 kwietnia 2025

EA - Coupling i Kohezja

Każdy programista słyszał o zasadach dobrego kodu: czytelność, modularność, łatwość testowania, niskie koszty utrzymania. Ale jak je osiągnąć?



Jednym z fundamentów są dwa niepozorne, lecz kluczowe pojęcia: kohezja (cohesion) i sprzężenie (coupling).

W tym artykule wyjaśnię, czym są, jak wpływają na jakość systemu i pokażę praktyczne przykłady.

Coupling (sprzężenie)

Sprzężenie to miara zależności między modułami/kontrolerami/klasami.

  • Niskie sprzężenie (loose coupling) – moduły są niezależne, zmiana jednego nie powoduje efektu domina.

  • Wysokie sprzężenie (tight coupling) – zmiana w jednym komponencie wymusza zmiany w wielu innych, co utrudnia rozwój i testowanie.

Przykład wysokiego sprzężenia:


class OrderService { private final EmailService emailService = new EmailService(); public void placeOrder(Order order) { // logika zamówienia emailService.sendConfirmation(order.getUserEmail()); } }

OrderService tworzy instancję EmailService i staje się od niej zależny. Nie można tego łatwo przetestować, wymienić implementacji, ani odseparować.

Przykład niskiego sprzężenia (dobrze):


class OrderService { private final EmailService emailService; public OrderService(EmailService emailService) { this.emailService = emailService; } public void placeOrder(Order order) { // logika zamówienia emailService.sendConfirmation(order.getUserEmail()); } }

Tu zastosowano wstrzykiwanie zależności (Dependency Injection). OrderService nie obchodzi, jaka to implementacja EmailService. Dzięki temu łatwiej pisać testy, refaktoryzować i utrzymywać kod.

Cohesion (kohezja, spójność)

Kohezja to miara jak bardzo odpowiedzialności danego modułu są ze sobą powiązane.

  • Wysoka kohezja – klasa/metoda ma jedno konkretne zadanie.

  • Niska kohezja – klasa robi wszystko: zapisuje dane, wysyła e-maile, liczy podatki i jeszcze zapisuje logi...

Przykład niskiej kohezji:


class ReportManager { public void generateReport() { /* ... */ } public void sendReportByEmail() { /* ... */ } public void logReportToFile() { /* ... */ } public void calculateRevenue() { /* ... */ } }

Ta klasa ma wiele odpowiedzialności. Trudno ją rozszerzyć lub przetestować.

Przykład wysokiej kohezji (dobrze):


class ReportGenerator { public Report generate() { /* ... */ } } class ReportSender { public void send(Report report) { /* ... */ } } class RevenueCalculator { public BigDecimal calculate(Report report) { /* ... */ } }

Każda klasa ma jedno zadanie (Single Responsibility Principle) i dobrze współpracuje z innymi – ale nie jest z nimi nadmiernie powiązana. To przykład wysokiej kohezji i niskiego sprzężenia – złotego standardu projektowania.

Dlaczego to ważne?

Cecha systemuNiskie sprzężenieWysoka kohezja
Testowalność✅ Łatwa✅ Łatwa
Rozszerzalność✅ Modułowa✅ Przewidywalna
Refaktoryzacja✅ Bezpieczna✅ Czysta
Zrozumiałość kodu✅ Izolowana✅ Logiczna

Jak to osiągnąć?

✅ Stosuj wstrzykiwanie zależności
✅ Dziel kod zgodnie z zasadą jednej odpowiedzialności (SRP)
✅ Używaj wzorców projektowych (np. Strategy, Adapter)
✅ Grupuj klasy zgodnie z kontekstem domeny (np. DDD)
✅ Refaktoruj, gdy widzisz "kuchnię z tysiącem zadań"

Podsumowanie

  • Coupling to pytanie: jak bardzo zależysz od innych?

  • Cohesion to pytanie: czy robisz jedną rzecz dobrze, czy wszystko po trochu?

Dążymy do niskiego sprzężenia i wysokiej kohezji – to podstawa dobrze zaprojektowanych, testowalnych i skalowalnych systemów.

poniedziałek, 14 kwietnia 2025

EA SAP HANA - Architektura i technologia

 SAP HANA to nowoczesna platforma, która łączy funkcje relacyjnej bazy danych z przetwarzaniem w czasie rzeczywistym, analizą danych i wsparciem dla aplikacji. Jej siła tkwi nie tylko w wydajności, ale przede wszystkim w przemyślanej, modułowej architekturze i zastosowaniu technologii in-memory oraz przetwarzania kolumnowego.



Model In-Memory – serce SAP HANA

Co to znaczy "in-memory"?

W tradycyjnych bazach danych dane są odczytywane z dysku, co generuje opóźnienia. SAP HANA trzyma dane w całości w pamięci RAM, umożliwiając natychmiastowy dostęp i przetwarzanie. To drastycznie skraca czas wykonywania operacji – nawet z godzin do sekund.

Główne zalety:

  • Minimalne czasy dostępu do danych

  • Brak konieczności indeksowania – oszczędność miejsca i uproszczona architektura

  • Możliwość uruchamiania analiz w czasie rzeczywistym (Real-Time Analytics)

Przechowywanie danych: Row Store vs Column Store

SAP HANA obsługuje dwa typy przechowywania danych:

Row Store (przechowywanie wierszowe)

  • Klasyczna forma znana z tradycyjnych RDBMS

  • Lepsza wydajność dla operacji OLTP (insert/update/delete)

  • Stosowana głównie do tabel tymczasowych lub o małym rozmiarze

Column Store (przechowywanie kolumnowe)

  • Dane są przechowywane w kolumnach, co umożliwia:

    • Kompresję danych

    • Szybsze przetwarzanie zapytań analitycznych (OLAP)

    • Równoległe operacje na wielu rdzeniach CPU

Fakt: Większość tabel w SAP HANA (zwłaszcza analitycznych) wykorzystuje Column Store.

Architektura systemowa SAP HANA

System SAP HANA składa się z wielu komponentów (procesów), które wspólnie zapewniają jego funkcjonalność. Poniżej najważniejsze z nich:

Kluczowe procesy SAP HANA

ProcesOpis
Index ServerObsługuje zapytania SQL, transakcje, autoryzacje, przechowuje dane
Name ServerZarządza metadanymi i partycjonowaniem danych w klastrze
Preprocessor ServerWspiera przetwarzanie tekstu (analiza dokumentów, pełnotekstowe wyszukiwanie)
Statistics ServerDostarcza statystyki systemowe i monitoruje wydajność
Web DispatcherLoad balancer dla żądań HTTP/HTTPS
XS Advanced EngineŚrodowisko uruchomieniowe dla aplikacji uruchamianych na HANA

W systemie wielowęzłowym (scale-out) niektóre z tych procesów są powielane i rozdzielane między hosty.

SAP HANA jako platforma dla aplikacji

SAP HANA to nie tylko baza danych – może również pełnić rolę platformy aplikacyjnej, umożliwiając uruchamianie kodu bezpośrednio w bazie (tzw. code push-down). Przykłady:

  • Procedury SQLScript – logika biznesowa wykonywana bezpośrednio w HANA

  • Modelowanie danych – widoki analityczne, calculation views

  • XS Advanced – środowisko do tworzenia nowoczesnych aplikacji webowych w HANA

Dzięki temu możliwa jest budowa aplikacji full-stack bez konieczności korzystania z oddzielnego serwera aplikacyjnego.

Wydajność i skalowalność

Skalowalność pionowa (scale-up)

  • Zwiększanie mocy pojedynczej maszyny (więcej CPU, RAM)

  • Prosta konfiguracja, niskie opóźnienia

  • Ograniczenia sprzętowe

5.2. Skalowalność pozioma (scale-out)

  • Dodawanie kolejnych hostów do klastra HANA

  • Wymaga zaawansowanego zarządzania danymi (partitioning, rebalancing)

  • Idealne dla przetwarzania Big Data

Przetwarzanie danych w czasie rzeczywistym

SAP HANA umożliwia integrację z wieloma źródłami danych i ich analizę w czasie rzeczywistym:

  • Strumieniowanie danych (SAP Smart Data Streaming)

  • Replikacja danych (SAP SLT)

  • Wbudowane algorytmy analityczne, predykcyjne i tekstowe

Bezpieczeństwo i niezawodność

SAP HANA oferuje szeroki zestaw funkcji w zakresie bezpieczeństwa:

  • Autoryzacja oparta na rolach (RBAC)

  • Audyt i logowanie działań użytkowników

  • Szyfrowanie danych (data-at-rest, data-in-transit)

  • Wysoka dostępność (High Availability) i replikacja systemowa

Podsumowanie

SAP HANA to coś więcej niż tylko szybka baza danych. To kompletna, skalowalna platforma danych, łącząca przetwarzanie transakcyjne i analityczne, obsługująca zaawansowaną logikę aplikacyjną oraz integrację z nowoczesnymi systemami IT.

Dzięki przemyślanej architekturze, SAP HANA pozwala firmom przyspieszyć decyzje biznesowe, zwiększyć wydajność operacyjną i wdrażać innowacje z niespotykaną dotąd szybkością.

poniedziałek, 7 kwietnia 2025

Wzorce MSA - Distributed Tracing B3 propagation

 W architekturze mikroserwisów każdy system to zbiór wielu mniejszych komponentów, które komunikują się między sobą najczęściej za pomocą żądań HTTP, wiadomości z kolejek lub gRPC. To świetnie wspiera skalowalność i elastyczność… dopóki coś nie przestanie działać.


Wtedy pojawia się pytanie:
  • Gdzie utknęło żądanie?
  • Który mikroserwis zawiódł?
  • Jak długo trwało przetwarzanie w każdym kroku?

Distributed Tracing to wzorzec, który pozwala śledzić żądania przez cały łańcuch mikroserwisów, a standard B3 umożliwia łatwą i jednolitą identyfikację każdego kroku tej podróży.

Na czym polega Distributed Tracing?

Distributed Tracing (śledzenie rozproszone) to mechanizm, który rejestruje ścieżkę żądania przez wiele usług. Każde żądanie otrzymuje unikalny identyfikator, który jest przekazywany między usługami — dzięki temu możemy odtworzyć całą trasę żądania i zmierzyć czas trwania każdego kroku.

Wprowadzenie do standardu B3

B3  to lekki, prosty do wdrożenia standard propagowania trace-id w systemach rozproszonych. Wystarczy dodać kilka nagłówków HTTP, by rozpocząć śledzenie żądań.

Kluczowe nagłówki B3:

NagłówekOpis
X-B3-TraceIdUnikalny identyfikator całego śledzenia (trace)
X-B3-SpanIdIdentyfikator konkretnego kroku (span) w ramach trace
X-B3-ParentSpanId(opcjonalnie) identyfikator nadrzędnego span
X-B3-SampledCzy trace ma być zarejestrowany (1 = tak)
X-B3-FlagsDodatkowa flaga debugowania

Przykład nagłówków B3 w żądaniu HTTP:


X-B3-TraceId: 4bf92f3577b34da6a3ce929d0e0e4736 X-B3-SpanId: 00f067aa0ba902b7 X-B3-ParentSpanId: b73a56a1d2ee0eb2 X-B3-Sampled: 1

Jak to działa w praktyce?

Schemat przepływu

Wyobraźmy sobie system z trzema mikroserwisami:


Klient → Gateway → Serwis A → Serwis B → Serwis C

Dzięki nagłówkom B3:

  1. Klient wysyła żądanie do Gateway z X-B3-TraceId.

  2. Gateway tworzy X-B3-SpanId i wysyła je do Serwisu A.

  3. Serwis A generuje swój SpanId, zachowuje ParentSpanId z Gatewaya i przekazuje trace dalej.

  4. Na końcu, Serwis C kończy trace — a wszystkie kroki są zarejestrowane.

Wizualizacja śladu (trace)

Po zebraniu wszystkich danych (np. za pomocą Jaeger, Zipkin lub Grafana Tempo), możemy zobaczyć wizualnie:


TraceId: 4bf92f3577b34da6a3ce929d0e0e4736 ├── Gateway [15 ms] │ └── Serwis A [30 ms] │ └── Serwis B [40 ms] │ └── Serwis C [25 ms]

Implementacja Distributed Tracing z użyciem Spring Boot i Sleuth


<!-- pom.xml --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>

# application.yml spring: zipkin: base-url: http://zipkin:9411 sleuth: sampler: probability: 1.0

Dzięki Sleuth i Zipkin aplikacja automatycznie:

  • generuje trace-id i span-id,

  • przekazuje nagłówki HTTP,

  • wysyła dane do Zipkina.

Korzyści z wdrożenia Distributed Tracing

Szybsze diagnozowanie problemów
Pomiar czasu odpowiedzi poszczególnych usług
Widoczność zależności między mikroserwisami
Identyfikacja wąskich gardeł
Lepsze wsparcie operacyjne i DevOps


Wnioski i dobre praktyki

  • Zawsze przekazuj trace-id i span-id w żądaniach HTTP, gRPC i eventach.
  • Testuj czy wszystkie mikroserwisy poprawnie obsługują nagłówki B3.
  • Używaj narzędzi jak Zipkin, Jaeger lub Grafana Tempo do analizy trace’ów.
  • W systemach rozproszonych bez observability jesteś ślepy.


Podsumowanie

Wzorzec Distributed Tracing to fundament nowoczesnych, obserwowalnych systemów mikroserwisowych. W połączeniu ze standardem B3 umożliwia łatwą propagację identyfikatorów śledzenia między usługami. Dobrze wdrożony trace pozwala nie tylko znaleźć błędy, ale też optymalizować wydajność i rozumieć złożoność architektury.