Tam, gdzie widzisz zło, nierówność lub niesprawiedliwość,
mów o tym otwarcie, bo to jest twój kraj.
To jest twoja demokracja. Twórz ją. Chroń. Dziel się nią.

Thurgood Marshall, sędzia Sądu Najwyższego

Dane oryginału

Original edition copyright © 2019 by Chris Richardson. All rights reserved. Title of English-language original: Microservices Patterns: With examples in Java by Chris Richardson, ISBN 9781617294549. Polish-language edition © 2020 by Polish Scientific Publishers PWN Wydawnictwo Naukowe PWN Spółka Akcyjna. All Rights reserved.

Przekład Magdalena Rogulska i Mariusz Rogulski na zlecenie WITKOM Witold Sikorski

Projekt okładki polskiego wydania INT-MEDIA Dawid Mazur na podstawie oryginału

Wydawca Wioleta Szczygielska-Dybciak

Redaktor prowadzący Jolanta Kowalczuk

Redaktor Jolanta Sierakowska

Koordynator produkcji Anna Bączkowska

Skład wersji elektronicznej na zlecenie Wydawnictwa Naukowego PWN

Monika Lipiec /Woblink

Zastrzeżonych nazw firm i produktów użyto w książce wyłącznie w celu identyfikacji.

Książka, którą nabyłeś, jest dziełem twórcy i wydawcy. Prosimy, abyś przestrzegał praw, jakie im przysługują. Jej zawartość możesz udostępnić nieodpłatnie osobom bliskim lub osobiście znanym. Ale nie publikuj jej w internecie. Jeśli cytujesz jej fragmenty, nie zmieniaj ich treści i koniecznie zaznacz, czyje to dzieło. A kopiując jej część, rób to jedynie na użytek osobisty.

Szanujmy cudzą własność i prawo.

Więcej na www.legalnakultura.pl

Polska Izba Książki

Copyright © for the Polish edition by Wydawnictwo Naukowe PWN SA

Warszawa 2020

ISBN 978-83-01-21223-0

eBook został przygotowany na podstawie wydania papierowego z 2020 r., (wyd. I)

Warszawa 2020

Wydawnictwo Naukowe PWN SA

02-460 Warszawa, ul. Gottlieba Daimlera 2

tel. 22 69 54 321, faks 22 69 54 288

infolinia 801 33 33 88

e-mail: pwn@pwn.com.pl; reklama@pwn.pl

www.pwn.pl

Spis treści

przedmowa

podziękowania

o książce

o autorze

o ilustracji na okładce

1. Ucieczka z monolitycznego piekła

1.1. Powolny marsz w kierunku monolitycznego piekła

1.1.1. Architektura aplikacji FTGO

1.1.2. Zalety architektury monolitycznej

1.1.3. Życie w monolitycznym piekle

Kompleksowość przeraża deweloperów

Rozwój jest powolny

Ścieżka od zatwierdzenia do wdrożenia jest długa i mozolna

Skalowanie jest trudne

Dostarczanie niezawodnego monolitu jest wyzwaniem

Blokada w coraz bardziej przestarzałym stosie technologicznym

1.2. Dlaczego ta książka jest dla ciebie odpowiednia

1.3. Czego się nauczysz z tej książki?

1.4. Architektura mikroserwisowa na ratunek

1.4.1. Sześcian skal i mikroserwisy

Skalowanie w osi X równoważy obciążenia dotyczące żądań między wiele instancji

Skalowanie w osi Z trasuje żądania na podstawie atrybutu żądania

Skalowanie w osi Y funkcjonalnie rozkłada aplikację na usługi

1.4.2. Mikroserwisy jako forma modułowości

1.4.3. Każda usługa ma własną bazę danych

1.4.4. Architektura mikroserwisowa FTGO

1.4.5. Porównanie architektury mikroserwisowej i SOA

1.5. Zalety i wady architektury mikroserwisowej

1.5.1. Zalety architektury mikroserwisowej

Umożliwianie ciągłego dostarczania i wdrażania dużych, złożonych aplikacji

Każda usługa jest mała i łatwa w utrzymaniu

Usługi są niezależnie skalowalne

Lepsza izolacja błędów

Łatwe eksperymentowanie i wdrażanie nowych technologii

1.5.2. Wady architektury mikroserwisowej

Znalezienie odpowiednich usług jest trudne

Systemy rozproszone są złożone

Wdrażanie funkcjonalności obejmujących wiele usług wymaga starannej koordynacji

Decyzja o przyjęciu jest trudna

1.6. Język wzorców architektury mikroserwisowej

1.6.1. Architektura mikroserwisowa nie jest złotym środkiem

1.6.2. Wzorce i języki wzorców

Wzorce powiązane: pięć różnych rodzajów relacji

1.6.3. Omówienie języka wzorców architektury mikroserwisowej

Wzorce do dekompozycji aplikacji na usługi

Wzorce komunikacyjne

Wzorce spójności danych do wdrażania zarządzania transakcjami

Wzorce odpytywania danych w architekturze mikroserwisowej

Wzorce wdrażania usług

Wzorce obserwowalności zapewniają wgląd w zachowanie aplikacji

Wzorce automatycznego testowania usług

Wzorce do obsługi potrzeb przekrojowych

Wzorce bezpieczeństwa

1.7. Poza mikroserwisy: proces i organizacja

1.7.1. Tworzenie oprogramowania i organizacja dostarczania

1.7.2. Proces tworzenia i dostarczania oprogramowania

1.7.3. Ludzka strona wprowadzania mikroserwisów

2. Strategie dekompozycyjne

2.1. Czym dokładnie jest architektura mikroserwisowa?

2.1.1. Czym jest architektura oprogramowania i dlaczego ma ona znaczenie?

Definicja architektury oprogramowania

Model perspektyw „4+1” w architekturze oprogramowania

Dlaczego architektura jest ważna

2.1.2. Przegląd stylów architektonicznych

Styl: architektura warstwowa

Styl: architektura heksagonalna

2.1.3. Architektura mikroserwisowa jest stylem architektonicznym

Co to jest usługa?

Czym są luźne powiązania?

Rola współdzielonych bibliotek

Rozmiar usługi jest w zasadzie nieważny

2.2. Definiowanie architektury mikroserwisowej aplikacji

2.2.1. Identyfikowanie operacji systemu

Tworzenie wysokopoziomowego modelu domeny

Definiowanie operacji systemu

2.2.2. Definiowanie usług z zastosowaniem wzorca dekompozycji według zdolności biznesowych

Zdolności biznesowe określają, co robi organizacja

Identyfikowanie zdolności biznesowych

Od zdolności biznesowych do usług

2.2.3. Definiowanie usług z zastosowaniem dekompozycji według wzorca subdomeny

2.2.4. Wytyczne dotyczące dekompozycji

Zasada pojedynczej odpowiedzialności

Reguła wspólnego domknięcia

2.2.5. Przeszkody w dekompozycji aplikacji na usługi

Opóźnienia sieciowe

Synchroniczna komunikacja międzyprocesowa ogranicza dostępność

Utrzymanie spójności danych w usługach

Uzyskanie spójnego widoku danych

Dekompozycję utrudniają god classes

2.2.6. Tworzenie API usług

Przypisywanie operacji systemu do usług

Określanie API wymaganego do realizowania współpracy między usługami

3. Komunikacja międzyprocesowa w architekturze mikroserwisowej

3.1. Przegląd komunikacji międzyprocesowej w architekturze mikroserwisowej

3.1.1. Style interakcji

3.1.2. Definiowanie API w architekturze mikroserwisowej

3.1.3. Ewolucja API

Użycie semantycznego wersjonowania

Wprowadzanie drobnych, kompatybilnych wstecznie zmian

Wprowadzanie istotnych zmian

3.1.4. Formaty komunikatów

Tekstowe formaty komunikatów

Binarne formaty komunikatów

3.2. Komunikacja z użyciem wzorca synchronicznego zdalnego wywołania procedury

3.2.1. Korzystanie z REST

Model dojrzałości REST

Specyfikowanie REST API

Wyzwania związane z pobieraniem wielu zasobów w jednym żądaniu

Wyzwania związane z mapowaniem operacji na czasowniki HTTP

Zalety i wady REST

3.2.2. Korzystanie z gRPC

3.2.3. Postępowanie w przypadku częściowej awarii z użyciem wzorca bezpiecznika

Tworzenie solidnych proxy RPI

Reagowanie na niedostępne usługi

3.2.4. Użycie wykrywania usług

Wykrywanie usług

Stosowanie wzorców wykrywania usług na poziomie aplikacji

Stosowanie wzorców wykrywania usług na poziomie platformy

3.3. Komunikacja z użyciem wzorca asynchronicznego przesyłania komunikatów

3.3.1. Spojrzenie na przesyłanie komunikatów

O komunikatach

O kanałach komunikatów

3.3.2. Realizacja stylów interakcji za pomocą przesyłania komunikatów

Realizacja żądanie/odpowiedź i asynchroniczne żądanie/odpowiedź

Realizacja powiadomień jednokierunkowych

Realizacja publikuj/subskrybuj

Realizacja publikowanie/asynchroniczna odpowiedź

3.3.3. Tworzenie specyfikacji dla API usługi opartego na przesyłaniu komunikatów

Dokumentowanie operacji asynchronicznych

Dokumentowanie publikowanych zdarzeń

3.3.4. Użycie brokera komunikatów

Przesyłanie komunikatów bez brokera

Przesyłanie komunikatów oparte na brokerze

Realizacja kanałów komunikatów za pomocą brokera

Zalety i wady przesyłania komunikatów przez brokerów

3.3.5. Konkurujący odbiorcy i sortowanie komunikatów

3.3.6. Obsługa zduplikowanych komunikatów

Tworzenie idempotentnej obsługi komunikatów

Śledzenie komunikatów i odrzucanie duplikatów

3.3.7. Komunikaty transakcyjne

Użycie tabeli bazy danych jako kolejki komunikatów

Publikowanie zdarzeń z użyciem wzorca publikatora z przeszukiwaniem

Publikowanie zdarzeń z zastosowaniem wzorca śledzenie dziennika transakcji

3.3.8. Biblioteki i frameworki do przesyłania komunikatów

Proste przesyłanie komunikatów

Publikowanie zdarzeń domenowych

Przesyłanie komunikatów oparte na poleceniu/odpowiedzi

3.4. Użycie asynchronicznego przesyłania komunikatów w celu poprawy dostępności

3.4.1. Komunikacja synchroniczna zmniejsza dostępność

3.4.2. Eliminowanie interakcji synchronicznych

Użycie asynchronicznych stylów interakcji

Replikacja danych

Zakończenie przetwarzania po zwróceniu odpowiedzi

4. Zarządzanie transakcjami z użyciem sag

4.1. Zarządzanie transakcjami w architekturze mikroserwisowej

4.1.1. Potrzeba transakcji rozproszonych w architekturze mikroserwisowej

4.1.2. Problem z rozproszonymi transakcjami

4.1.3. Użycie wzorca sagi do zarządzania spójnością danych

Przykładowa saga: Create Order Saga

Do wycofania zmian sagi wykorzystują transakcje kompensujące

4.2. Koordynowanie sag

4.2.1. Sagi oparte na choreografii

Realizacja Create Order Saga z wykorzystaniem choreografii

Niezawodna komunikacja oparta na zdarzeniach

Zalety i wady sag opartych na choreografii

4.2.2. Sagi oparte na orkiestracji

Realizacja Create Order Saga z wykorzystaniem orkiestracji

Modelowanie orkiestratorów sagi jako maszyn stanów

Orkiestracja sagi i komunikaty transakcyjne

Zalety i wady sag opartych na orkiestracji

4.3. Radzenie sobie z brakiem izolacji

4.3.1. Przegląd anomalii

Utracone aktualizacje

Brudne odczyty

4.3.2. Przeciwdziałania związane z brakiem izolacji

Struktura sagi

Blokada semantyczna

Aktualizacje przemienne

Podejście pesymistyczne

Ponowne odczytanie wartości

Rejestr kroków

Według korzyści

4.4. Projekt usługi Order Service i sagi Create Order Saga

4.4.1. Klasa OrderService

4.4.2. Implementacja Create Order Saga

Orkiestrator CreateOrderSaga

Klasa CreateOrderSagaState

Klasa KitchenServiceProxy

Framework Eventuate Tram Saga

4.4.3. Klasa OrderCommandHandlers

4.4.4. Klasa OrderServiceConfiguration

5. Projektowanie logiki biznesowej w architekturze mikroserwisowej

5.1. Wzorce organizacji logiki biznesowej

5.1.1. Projektowanie logiki biznesowej z użyciem wzorca skryptu transakcji

5.1.2. Projektowanie logiki biznesowej z użyciem wzorca modelu domeny

5.1.3. Domain-Driven Design

5.2. Projektowanie modelu domeny z użyciem wzorca agregatu DDD

5.2.1. Problem z rozmytymi granicami

5.2.2. Agregaty mają wyraźne granice

Agregaty są granicami spójności

Kluczowa jest identyfikacja agregatów

5.2.3. Zasady agregatów

Zasada 1: Odniesienie tylko do korzenia agregatu

Zasada 2: Odniesienia między agregatami muszą korzystać z kluczy głównych

Zasada 3: Jedna transakcja tworzy lub aktualizuje jeden agregat

5.2.4. Ziarnistość agregatu

5.2.5. Projektowanie logiki biznesowej z agregatami

5.3. Publikowanie zdarzeń domenowych

5.3.1. Dlaczego publikować zdarzenia związane ze zmianą stanu?

5.3.2. Czym jest zdarzenie domenowe?

5.3.3. Wzbogacanie zdarzeń

5.3.4. Identyfikowanie zdarzeń domenowych

5.3.5. Generowanie i publikowanie zdarzeń domenowych

Generowanie zdarzeń domenowych

Jak niezawodnie publikować zdarzenia domenowe?

5.3.6. Pobieranie zdarzeń domenowych

5.4. Logika biznesowa Kitchen Service

5.4.1. Agregat Ticket

Struktura klasy Ticket

Zachowanie agregatu Ticket

Usługa domenowa KitchenService

Klasa KitchenServiceCommandHandler

5.5. Logika biznesowa Order Service

5.5.1. Agregat Order

Struktura agregatu Order

Maszyna stanów agregatu Order

Metody agregatu Order

5.5.2. Klasa OrderService

6. Rozwijanie logiki biznesowej za pomocą pozyskiwania zdarzeń

6.1. Wykorzystywanie wzorca pozyskiwania zdarzeń do tworzenia logiki biznesowej

6.1.1. Problemy tradycyjnego utrwalania

Niedopasowanie między modelem obiektowym a relacyjnym

Brak historii agregatu

Wdrożenie dzienników zdarzeń jest uciążliwe i podatne na błędy

Publikowanie zdarzeń jest powiązane z logiką biznesową

6.1.2. Pozyskiwanie zdarzeń

Pozyskiwanie zdarzeń utrwala agregaty korzystając ze zdarzeń

Zdarzenia reprezentują zmiany stanu

Metody agregatów dotyczą zdarzeń

Agregat Order wykorzystujący pozyskiwanie zdarzeń

6.1.3. Obsługa jednoczesnych aktualizacji z użyciem optymistycznego blokowania

6.1.4. Pozyskiwanie zdarzeń i publikowanie zdarzeń

Użycie przeszukiwania do publikowania zdarzeń

Wykorzystanie śledzenia dziennika transakcji do niezawodnego publikowania zdarzeń

6.1.5. Użycie migawek do poprawy wydajności

6.1.6. Idempotentne przetwarzanie komunikatów

Idempotentne przetwarzanie komunikatów z magazynem zdarzeń opartym na RDBMS

Idempotentne przetwarzanie komunikatów z magazynem zdarzeń opartym na NoSQL

6.1.7. Rozwój zdarzeń domenowych

Ewolucja schematu zdarzeń

Zarządzanie zmianami schematu przez rzutowanie w górę

6.1.8. Zalety pozyskiwania zdarzeń

Niezawodnie publikowanie zdarzeń domenowych

Zachowywanie historii agregatów

Zwykle pomaga uniknąć problemu niedopasowania między modelem relacyjnym a obiektowym

Oferuje programistom maszynę czasu

6.1.9. Wady pozyskiwania zdarzeń

Inny model programowania powodujący wzrost krzywej uczenia się

Złożoność aplikacji opartej na przesyłaniu komunikatów

Ewolucja zdarzeń może być trudna

Usuwanie danych jest trudne

Wykonywanie zapytań do magazynu zdarzeń jest trudne

6.2. Implementowanie magazynu zdarzeń

6.2.1. Jak działa magazyn zdarzeń Eventuate Local

Schemat bazy danych zdarzeń Eventuate Local

Pobieranie zdarzeń przez subskrybowanie brokera zdarzeń Eventuate Local

Przekaźnik zdarzeń Eventuate Local propaguje zdarzenia z bazy danych do brokera komunikatów

6.2.2. Środowisko klienta Eventuate dla Javy

Definiowanie agregatów za pomocą klasy ReflectiveMutableCommandProcessingAggregate

Definiowanie poleceń agregatu

Definiowanie zdarzeń domenowych

Tworzenie, wyszukiwanie i aktualizacja agregatów za pomocą klasy AggregateRepository

Subskrybowanie zdarzeń domenowych

6.3. Łączne używanie sag i pozyskiwania zdarzeń

6.3.1. Realizacja sag opartych na choreografii z wykorzystaniem pozyskiwania zdarzeń

6.3.2. Tworzenie sagi opartej na orkiestracji

Tworzenie orkiestratora sagi podczas korzystania z magazynu zdarzeń opartego na RDMBS

Tworzenie orkiestratora sagi podczas korzystania z magazynu zdarzeń opartego na NoSQL

6.3.3. Tworzenie uczestnika sagi opartej na pozyskiwaniu zdarzeń

Idempotentna obsługa komunikatów poleceń

Atomowe wysyłanie komunikatów zwrotnych

Przykład uczestnika sagi opartej na pozyskiwaniu zdarzeń

6.3.4. Projektowanie orkiestratorów sagi z użyciem pozyskiwania zdarzeń

Utrzymywanie orkiestratora sagi z użyciem pozyskiwania zdarzeń

Niezawodne wysyłanie komunikatów poleceń

Dokładnie jednokrotne przetwarzanie odpowiedzi

7. Implementowanie zapytań w architekturze mikroserwisowej

7.1. Tworzenie zapytań z użyciem wzorca kompozycji API

7.1.1. Operacja zapytania findOrder()

7.1.2. Wzorzec kompozycji API

7.1.3. Implementowanie operacji zapytania findOrder() z wykorzystaniem wzorca kompozycji API

7.1.4. Problemy projektowe związane z wzorcem kompozycji API

Kto powinien odgrywać rolę API Composer?

7.1.5. Wady i zalety wzorca kompozycji API

Zwiększone koszty ogólne

Brak spójności danych transakcyjnych

7.2. Korzystanie z wzorca CQRS

7.2.1. Motywacja do korzystania z CQRS

Realizacja operacji zapytania findOrderHistory()

Wymagające zapytanie dotyczące pojedynczej usługi: findAvailableRestaurants()

Konieczność oddzielenia obowiązków

7.2.2. Przegląd CQRS

CQRS oddziela polecenia od zapytań

CQRS i usługi tylko dla zapytań

7.2.3. Zalety CQRS

7.2.4. Wady CQRS

Bardziej złożona architektura

Konieczność radzenia sobie z opóźnieniem replikacji

7.3. Projektowanie widoków CQRS

7.3.1. Wybór magazynu danych widoku

Bazy danych SQL kontra NoSQL

Wspieranie operacji aktualizacji

7.3.2. Projekt modułu dostępu do danych

Obsługa jednoczesnych aktualizacji

Idempotentne procedury obsługi zdarzeń

Umożliwienie aplikacji klienckiej korzystania z ostatecznie spójnego widoku

7.3.3. Dodawanie i aktualizacja widoków CQRS

Budowanie widoków CQRS z użyciem zarchiwizowanych zdarzeń

Stopniowe budowanie widoków CQRS

7.4. Implementowanie widoku CQRS za pomocą AWS DynamoDB

7.4.1. Moduł OrderHistoryEventHandlers

7.4.2. Modelowanie danych i projektowanie zapytań za pomocą DynamoDB

Projektowanie tabeli ftgo-order-history

Zdefiniowanie indeksu dla zapytania findOrderHistory

Realizacja zapytania findOrderHistory

Stronicowanie wyników zapytania

Aktualizowanie zamówień

Wykrywanie zduplikowanych zdarzeń

7.4.3. Klasa OrderHistoryDaoDynamoDb

Metoda addOrder()

Metoda notePickedUp()

Metoda idempotentUpdate()

Metoda findOrderHistory()

8. Wzorce zewnętrznych API

8.1. Problemy z projektowaniem zewnętrznego API

8.1.1. Problemy z projektowaniem API dla mobilnego klienta FTGO

Gorszy komfort użytkowania spowodowany klientem wykonującym wiele zapytań

Brak enkapsulacji wymaga od programistów frontendu, aby zmieniali swój kod krok w krok ze zmianami w backendzie

Usługi mogą wykorzystywać mechanizmy IPC nieprzyjazne dla klientów

8.1.2. Problemy z projektowaniem API dla innych typów klientów

Problemy z projektowaniem API dla aplikacji webowych

Problemy z projektowaniem API dla aplikacji JavaScript działających w przeglądarce

Projektowanie API dla aplikacji zewnętrznych

8.2. Wzorzec bramy API

8.2.1. Wzorzec bramy API

Przekierowywanie żądań

Kompozycja API

Translacja protokołów

Brama API oferuje każdemu klientowi specyficzne API

Implementowanie funkcji brzegowych

Architektura bramy API

Model własności bramy API

Korzystanie z wzorca Backends for Frontends

8.2.2. Zalety i wady bramy API

Zalety bramy API

Wady bramy API

8.2.3. Netflix jako przykład bramy API

8.2.4. Problemy z projektowaniem bramy API

Wydajność i skalowalność

Użycie abstrakcji programowania reaktywnego

Postępowanie w przypadku częściowej awarii

Bycie dobrym obywatelem w architekturze aplikacji

8.3. Implementowanie bramy API

8.3.1. Skorzystanie z gotowego produktu/usługi bramy API

Brama API AWS

AWS Application Load Balancer

Użycie produktu typu brama API

8.3.2. Opracowanie własnej bramy API

Użycie Netflix Zuul

O Spring Cloud Gateway

Klasa OrderConfiguration

Klasa OrderHandlers

Klasa OrderService

Klasa ApiGatewayApplication

8.3.3. Realizacja bramy API za pomocą GraphQL

Definiowanie schematu GraphQL

Wykonywanie zapytań GraphQL

Podłączanie schematu do danych

Optymalizacja ładowania za pomocą grupowania i buforowania

Integracja serwera Apollo GraphQL z Expressem

Tworzenie klienta GraphQL

9. Testowanie mikroserwisów: część 1

9.1. Strategie testowania w architekturze mikroserwisowej

9.1.1. Testowanie

Pisanie testów automatycznych

Testowanie z użyciem obiektów typu mock i stub

Różne rodzaje testów

Wykorzystanie kwadrantu testowego do kategoryzacji testów

Wykorzystanie piramidy testów jako przewodnika w wysiłkach związanych z testowaniem

9.1.2. Wyzwania związane z testowaniem mikroserwisów

Test kontraktowy ukierunkowany na klienta

Testowanie usług z wykorzystaniem Spring Cloud Contract

Konsumenckie testy kontraktowe API przesyłania komunikatów

9.1.3. Strumień wdrażania

9.2. Pisanie testów jednostkowych dla usługi

9.2.1. Projektowanie testów jednostkowych dla encji

9.2.2. Pisanie testów jednostkowych dla obiektów wartości

9.2.3. Opracowywanie testów jednostkowych dla sag

9.2.4. Pisanie testów jednostkowych dla usług domenowych

9.2.5. Opracowywanie testów jednostkowych dla kontrolerów

9.2.6. Pisanie testów jednostkowych dla procedur obsługi zdarzeń i komunikatów

10. Testowanie mikroserwisów: część 2

10.1. Pisanie testów integracyjnych

10.1.1. Testy integracyjne trwałości

10.1.2. Testy integracyjne interakcji żądanie/odpowiedź opartej na REST

Przykładowy kontrakt dla API REST

Integracyjny test kontraktowy ukierunkowany na klienta dla Order Service

Test integracyjny po stronie konsumenta dla OrderServiceProxy z API Gateway

10.1.3. Testy integracyjne interakcji publikuj/subskrybuj

Kontrakt dla publikowania zdarzenia OrderCreated

Test kontraktowy ukierunkowany na konsumenta dla Order Service

Test kontraktowy po stronie konsumenta dla Order History Service

10.1.4. Kontraktowe testy integracyjne interakcji asynchroniczne żądanie/odpowiedź

Przykładowy kontrakt dla asynchronicznego żądania/odpowiedzi

Kontraktowy test integracyjny po stronie konsumenta dla interakcji asynchroniczne żądanie/odpowiedź

Pisanie testów kontraktowych po stronie dostawcy przeznaczonych do testowania konsumenta w zakresie asynchronicznej interakcji żądanie/odpowiedź

10.2. Tworzenie testów komponentów

10.2.1. Definiowanie testów akceptacyjnych

10.2.2. Pisanie testów akceptacyjnych z użyciem Gherkina

Wykonywanie specyfikacji Gherkina za pomocą Cucumber

10.2.3. Projektowanie testów komponentów

Wewnątrzprocesowe testy komponentów

Pozaprocesowe testowanie komponentu

Jak tworzyć obiekty stubs dla usług w pozaprocesowych testach komponentów

10.2.4. Pisanie testów komponentów dla Order Service z FTGO

Klasa OrderServiceComponentTestStepDefinitions

Uruchamianie testów komponentów

10.3. Pisanie testów end-to-end

10.3.1. Projektowanie testów end-to-end

10.3.2. Pisanie testów end-to-end

10.3.3. Uruchamianie testów end-to-end

11. Opracowywanie usług gotowych do produkcji

11.1. Opracowywanie bezpiecznych usług

11.1.1. Przegląd kwestii bezpieczeństwa w tradycyjnej aplikacji monolitycznej

11.1.2. Realizacja kwestii bezpieczeństwa w architekturze mikroserwisowej

Obsługa uwierzytelniania w bramie API

Obsługa autoryzacji

Użycie JWT do przekazywania tożsamości użytkownika i roli

Użycie OAuth 2.0 w architekturze mikroserwisowej

11.2. Projektowanie konfigurowalnych usług

11.2.1. Użycie konfiguracji zewnętrznej opartej na udostępnianiu

11.2.2. Użycie konfiguracji zewnętrznej opartej na pobieraniu

11.3. Projektowanie obserwowalnych usług

11.3.1. Użycie wzorca API kontroli działania

Realizacja punktu końcowego kontroli działania

Wywołanie punktu końcowego kontroli działania

11.3.2. Stosowanie wzorca agregacji dzienników

W jaki sposób usługa generuje dziennik

Infrastruktura agregacji dzienników

11.3.3. Stosowanie wzorca rozproszonego śledzenia

Użycie biblioteki instrumentacji

Serwer rozproszonego śledzenia

11.3.4. Stosowanie wzorca metryk aplikacji

Zbieranie metryk na poziomie usługi

Dostarczanie metryk do usługi metryk

11.3.5. Stosowanie wzorca śledzenia wyjątków

11.3.6. Stosowanie wzorca dzienników inspekcji

Dodawanie kodu dzienników inspekcji do logiki biznesowej

Użycie programowania aspektowego

Użycie pozyskiwania zdarzeń

11.4. Tworzenie usług za pomocą wzorca szkieletów mikroserwisowych

11.4.1. Korzystanie ze szkieletu mikroserwisowego

11.4.2. Od szkieletu mikroserwisowego do service mesh

12. Wdrażanie mikroserwisów

12.1. Wdrażanie usług jako pakietów specyficznych dla języka

12.1.1. Zalety wzorca Usługa jako pakiet specyficzny dla języka

Szybkie wdrażanie

Efektywne wykorzystanie zasobów

12.1.2. Wady wzorca Usługa jako pakiet specyficzny dla języka

Brak enkapsulacji stosu technologicznego

Brak możliwości ograniczenia zasobów zużywanych przez instancję usługi

Brak izolacji podczas uruchamiania wielu instancji usługi na tej samej maszynie

Automatyczne określenie miejsca umieszczenia instancji usługi jest trudne

12.2. Wdrażanie usług z użyciem wzorca usługi jako maszyny wirtualnej

12.2.1. Zalety wdrażania usług jako maszyn wirtualnych

Obraz maszyny wirtualnej hermetyzuje stos technologiczny

Instancje usług są izolowane

Wykorzystanie wyrafinowanej infrastruktury chmurowej

12.2.2. Wady wdrażania usług jako maszyn wirtualnych

Mniej wydajne wykorzystanie zasobów

Wolne wdrożenia

Narzut związany z administracją systemem

12.3. Wdrażanie usług za pomocą wzorca usługi jako kontenera

12.3.1. Wdrażanie usług za pomocą Dockera

Budowanie obrazu Dockera

Wysyłanie obrazu Dockera do rejestru

Uruchamianie kontenera Dockera

12.3.2. Zalety wdrażania usług jako kontenerów

12.3.3. Wady wdrażania usług jako kontenerów

12.4. Wdrażanie aplikacji FTGO za pomocą Kubernetesa

12.4.1. Kubernetes

Architektura Kubernetesa

Kluczowe pojęcia Kubernetesa

12.4.2. Wdrażanie Restaurant Service w Kubernetesie

Tworzenie service w Kubernetesie

Wdrażanie bramy API

12.4.3. Wdrożenia bez przestojów

12.4.4. Użycie service mesh w celu oddzielenia wdrożenia od wydania

Service mesh Istio

Wdrażanie usługi za pomocą Istio

Tworzenie reguł routingu do wersji v1

Wdrażanie wersji 2 Consumer Service

Przekierowywanie części testowego ruchu do wersji 2

Kierowanie produkcyjnego ruchu do wersji 2

12.5. Wdrażanie usług za pomocą wzorca wdrożenia bezserwerowego

12.5.1. Bezserwerowe wdrażanie za pomocą AWS Lambda

12.5.2. Tworzenie funkcji lambda

12.5.3. Wywoływanie funkcji lambda

Obsługa żądań HTTP

Obsługa zdarzeń generowanych przez usługi AWS

Definiowanie zaplanowanych funkcji lambda

Wywoływanie funkcji lambda za pomocą żądania usługi sieciowej

12.5.4. Zalety użycia funkcji lambda

12.5.5. Wady użycia funkcji lambda

12.6. Wdrażanie usługi RESTful z użyciem AWS Lambda i AWS Gateway

12.6.1. Projekt AWS Lambda wersji Restaurant Service

Klasa FindRestaurantRequestHandler

Wstrzykiwanie zależności z użyciem klasy AbstractAutowiringHttpRequestHandler

Klasa AbstractHttpHandler

12.6.2. Pakowanie usługi jako pliku ZIP

12.6.3. Wdrażanie funkcji lambda z użyciem frameworka Serverless

13. Refaktoryzacja do mikroserwisów

13.1. Refaktoryzacja do mikroserwisów

13.1.1. Dlaczego warto refaktoryzować monolit?

13.1.2. Duszenie monolitu

Szybkie i częste prezentowanie wartości dodanej

Minimalizacja zmian w monolicie

Techniczne aspekty infrastruktury wdrożeniowej: nie potrzebujemy wszystkiego od razu

13.2. Strategie refaktoryzacji monolitu do mikroserwisów

13.2.1. Realizacja nowych funkcjonalności jako usług

Integracja nowej usługi z monolitem

Kiedy zaimplementować nową funkcjonalność jako usługę

13.2.2. Oddzielanie warstwy prezentacji od backendu

13.2.3. Wyodrębnianie zdolności biznesowych do usług

Podział modelu domeny

Refaktoryzacja bazy danych

Replikowanie danych w celu uniknięcia rozległych zmian

Jakie usługi wyodrębniać i kiedy

13.3. Projektowanie, w jaki sposób usługa i monolit będą współpracować

13.3.1. Projektowanie kodu integrującego

Projektowanie API kodu integrującego

Wybór stylu interakcji i mechanizmu IPC

Projektowanie Anti-Corruption Layer

Jak monolit publikuje i subskrybuje zdarzenia domenowe

13.3.2. Utrzymywanie spójności danych w usłudze i monolicie

Wyzwanie związane ze zmianą monolitu, aby wspierał transakcje kompensujące

Sagi nie zawsze wymagają od monolitu wspierania transakcji kompensujących

Określanie kolejności wyodrębniania usług w celu uniknięcia konieczności implementacji transakcji kompensujących w monolicie

13.3.3. Obsługa uwierzytelniania i autoryzacji

LoginHandler monolitu ustawia plik cookie USERINFO

Brama API mapuje plik cookie USERINFO na nagłówek Authorization

13.4. Wdrażanie nowej funkcjonalności jako usługi: obsługa niezrealizowanych zamówień

13.4.1. Projekt Delayed Delivery Service

13.4.2. Projektowanie kodu integrującego dla Delayed Delivery Service

Pobieranie informacji kontaktowych klienta z wykorzystaniem CustomerContactInfoRepository

Publikowanie i pobieranie zdarzeń domenowych Order i Restaurant

13.5. Rozbijanie monolitu: wyodrębnianie zarządzania dostawami

13.5.1. Przegląd istniejącej funkcjonalności związanej z zarządzaniem dostawami

13.5.2. Przegląd Delivery Service

13.5.3. Projekt modelu domeny Delivery Service

Określenie encji i ich pól, które są częścią zarządzania dostawami

Decydowanie, które dane przenieść do Delivery Service

Projekt logiki domeny Delivery Service

13.5.4. Projekt kodu integrującego Delivery Service

Projekt API Delivery Service

W jaki sposób Delivery Service będzie uzyskiwał dostęp do danych monolitu FTGO

W jaki sposób monolit FTGO będzie uzyskiwał dostęp do danych Delivery Service?

13.5.5. Dostosowanie monolitu FTGO do współpracy z Delivery Service

Definiowanie interfejsu DeliveryService

Refaktoryzacja monolitu w celu wywoływania interfejsu DeliveryService

Implementacja interfejsu DeliveryService

Przełączanie funkcjonalności

Przypisy

przedmowa

Jednym z moich ulubionych cytatów jest

Przyszłość jest już dziś, tylko nierówno rozłożona.

William Gibson, autor fantastyki naukowej

Istotą tego cytatu jest to, że nowe pomysły i technologie potrzebują czasu, aby rozpowszechnić się w społeczności i zostać ogólnie przyjęte. Dobrym przykładem powolnego rozprzestrzeniania się pomysłów jest historia odkrywania mikrousług. Zaczęło się w 2006 r., kiedy zainspirowany przemówieniem ewangelisty AWS, podążyłem ścieżką, która ostatecznie doprowadziła mnie do stworzenia Cloud Foundry. (Jedyną wspólną cechą z dzisiejszym Cloud Foundry jest nazwa.) Cloud Foundry to platforma odgrywająca rolę usługi (PaaS) do automatyzacji wdrażania aplikacji Java na EC2. Jak każda inna aplikacja, którą stworzyłem w Javie, moja Cloud Foundry miała architekturę monolityczną składającą się z jednego pliku WAR (Java Web Application Archive).

Połączenie różnorodnego i złożonego zestawu funkcji, takich jak udostępnianie, konfiguracja, monitorowanie i zarządzanie w monolicie, stworzyło wyzwania rozwojowe i operacyjne. Nie można było na przykład zmienić interfejsu użytkownika bez testowania i ponownego wdrażania całej aplikacji. W związku z tym, że komponent monitorowania i zarządzania opierał się na silniku złożonego przetwarzania zdarzeń (Complex Event Processing, CEP), który utrzymywał stan w pamięci, nie mogliśmy uruchomić wielu instancji aplikacji! To było żenujące, ale to, co mogę powiedzieć, to, że jestem programistą i że „kto jest bez grzechu, niech pierwszy rzuci kamieniem”.

Najwyraźniej aplikacja szybko przerosła swoją monolityczną architekturę. Ale jaka była alternatywa? Po pewnym czasie odpowiedź pojawiła się w społeczności programistów w firmach, takich jak eBay czy Amazon. Na przykład Amazon zaczął migrować z monolitu około 2002 r. (https://plus.google.com/110981030061712822816/posts/AaygmbzVeRq). Nowa architektura zastąpiła monolit kolekcją luźno powiązanych usług. Usługi są własnością tego, kogo Amazon nazywa two pizza teams, czyli zespołów wystarczająco małych, aby mogły zostać nakarmione dwiema pizzami.

Amazon zastosował tę architekturę, żeby przyspieszyć tempo opracowywania oprogramowania, tak by firma mogła szybciej wprowadzać innowacje i skuteczniej konkurować na rynku. Wyniki są imponujące: podobno Amazon wprowadza zmiany produkcyjne co 11,6 sekundy!

Na początku 2010 r., po moim przejściu do innych projektów, przyszłość architektury oprogramowania w końcu mnie dopadła. Właśnie wtedy przeczytałem książkę The Art of Scalability: Scalable Web Architecture, Processes and Organizations for the Modern Enterprise (Addison-Wesley Professional, 2009) autorstwa Michaela T. Fishera i Martina L. Abbotta. Kluczową ideą w tej książce jest sześcian skal, który, jak opisano w rozdziale 2, jest trójwymiarowym modelem skalowania aplikacji. Skalowanie w osi Y określone przez dekompozycję funkcjonalną rozkłada aplikację na usługi. Z perspektywy czasu było to dość oczywiste, ale dla mnie był to moment „aha”! Mógłbym rozwiązać problemy, przed którymi stanąłem dwa lata wcześniej, i zaprojektować Cloud Foundry jako zestaw usług!

W kwietniu 2012 r. wygłosiłem swoje pierwsze przemówienie na temat tego podejścia architektonicznego, zatytułowane „Decomposing Applications of Deployability and Scalability” (www.slideshare.net/chris.e.richardson/decomposing-applications-for-scalability-and-deployability-april-2012). W tamtym czasie nie było ogólnie przyjętego terminu na tego rodzaju architekturę. Czasami nazywałem to modułową architekturą poliglotyczną, ponieważ usługi można było pisać w różnych językach.

Ale w przypadku innego przykładu tego, jak przyszłość jest nierównomiernie rozłożona, termin „mikroserwis” został użyty podczas warsztatów architektury oprogramowania w 2011 r. do opisania tego rodzaju architektury (https://en.wikipedia.org/wiki/Microservices). Po raz pierwszy zetknąłem się z tym terminem wtedy, kiedy usłyszałem, jak Fred George wygłasza przemówienie na Oredevie 2013. I podobało mi się to!

W styczniu 2014 r. utworzyłem witrynę https://microservices.io w celu dokumentowania architektury i wzorców projektowych, jakie napotkałem. Następnie, w marcu 2014 r., James Lewis i Martin Fowler opublikowali post na blogu na temat mikroserwisów (https://martinfowler.com/articles/microservices.html). Popularyzując termin „mikroserwisy”, post na blogu spowodował, że społeczność programistów skonsolidowała się wokół tej koncepcji.

Idea małych, luźno powiązanych zespołów, szybko i niezawodnie rozwijających się i dostarczających mikroserwisy powoli rozpowszechnia się w społeczności programistów. Ale jest prawdopodobne, że ta wizja przyszłości różni się od współczesnej rzeczywistości. Obecnie wielkie, krytyczne biznesowo aplikacje firm to zazwyczaj duże monolity opracowywane przez duże zespoły. Wydania oprogramowania zdarzają się rzadko i często są bolesne dla wszystkich zainteresowanych. Dział IT często stara się nadążyć za potrzebami firmy. Możemy wtedy zastanawiać się, jak do licha można zastosować w takim przypadku architekturę mikroserwisową.

Celem tej książki jest odpowiedź na to pytanie. Pozwoli ona dobrze zrozumieć architekturę mikroserwisową, jej zalety i wady oraz kiedy z niej korzystać. Książka opisuje, w jaki sposób rozwiązać liczne wyzwania projektowe, w tym wyzwania zarządzania rozproszonymi danymi. Obejmuje także sposób refaktoryzacji aplikacji monolitycznej do architektury mikroserwisowej. Ale ta książka nie jest manifestem mikroserwisów. Zamiast tego jest zorganizowana wokół kolekcji wzorców. Wzorzec jest rozwiązaniem wielokrotnego użytku dla problemu występującego w określonym kontekście. Piękno wzorca polega na tym, że oprócz opisu zalet rozwiązania opisuje on także wady i problemy, które należy rozwiązać, aby pomyślnie wdrożyć rozwiązanie. Z mojego doświadczenia wynika, że tego rodzaju obiektywizm podczas myślenia o rozwiązaniach prowadzi do znacznie lepszego podejmowania decyzji. Mam nadzieję, że spodoba ci się ta książka i że nauczy cię, jak skutecznie rozwijać mikroserwisy.

podziękowania

Chociaż pisanie jest zajęciem samotniczym, potrzeba dużej liczby osób, aby zmienić surowe szkice w gotową książkę.

Po pierwsze, chcę podziękować Erinowi Twoheyowi i Michaelowi Stevensowi z Manninga za ich nieustające zachęty do napisania kolejnej książki. Chciałbym również podziękować moim redaktorom zajmującym się strukturą książki: Cynthii Kane i Marinie Michaels. Cynthia Kane umożliwiła mi rozpoczęcie i pracowała ze mną przez kilka pierwszych rozdziałów. Marina Michaels przejęła zadania Cynthii i pracowała ze mną do końca. Będę na zawsze jej wdzięczny za drobiazgową i konstruktywną krytykę moich rozdziałów. I chcę także podziękować reszcie zespołu Manninga, który był zaangażowany w publikację tej książki.

Chciałbym podziękować mojemu redaktorowi technicznemu Christianowi Mennerichowi, mojemu korektorowi technicznemu Andy’emu Milesowi oraz wszystkim moim zewnętrznym recenzentom: Andy’emu Kirschowi, Antonio Pessolano, Aregowi Melik-Adamyanowi, Cage’owi Slagelowi, Carlosowi Curotto, Drorowi Helperowi, Erosowi Pedriniemu, Hugo Cruzowi, Irinie Romanenko, Jesse’emu Rosalia, Joe’emu Justesenowi, Johnowi Guthrie, Keerthi Shetty, Michele Mauro, Paulowi Grenecowi, Pethuru Rajowi, Potito Coluccelliemu, Shobhie Iyer, Simeonowi Leyzerzon, Srihariemu Sridharanowi, Timowi Moore, Tony’emu Sweetsowi, Trentowi Whiteleyowi, Wesowi Shaddiksowi, Williamowi E. Wheelerowi i Zoltanowi Hamoriemu.

Pragnę również podziękować wszystkim, którzy kupili MEAP¹ i przekazali opinie na forum lub mnie bezpośrednio.

Chciałbym podziękować organizatorom i uczestnikom wszystkich konferencji i spotkań, na których przemawiałem, za możliwość zaprezentowania i zrewidowania moich pomysłów. Dziękuję też moim klientom, którym udzielałem konsultacji i których trenowałem, za umożliwienie realizacji moich pomysłów w praktyce.

Chcę także podziękować moim kolegom Andrew, Valentinowi, Artemowi i Stanislavowi z Eventuate, Inc. za ich wkład w stworzenie produktu Eventuate i projekty open source.

Na koniec pragnąłbym podziękować mojej żonie Laurie i moim dzieciom, Ellie, Thomasowi i Janet, za ich wsparcie i zrozumienie w ciągu ostatnich 18 miesięcy. Podczas gdy byłem przyklejony do laptopa, przegapiłem chodzenie na mecze piłki nożnej Ellie, obserwowanie, jak Thomas uczy się latać na swoim symulatorze lotów, i testowanie nowych restauracji z Janet.

Dziękuję Wam wszystkim!

o książce

Celem tej książki jest nauczenie, jak z powodzeniem tworzyć aplikacje za pomocą architektury mikroserwisowej.

Nie tylko omawia ona zalety architektury mikroserwisowej, ale także opisuje jej wady. Dowiemy się, kiedy powinniśmy rozważyć użycie architektury monolitycznej i kiedy warto skorzystać z mikroserwisów.

Kto powinien przeczytać tę książkę

Ta książka koncentruje się na architekturze i rozwoju. Jest przeznaczona dla wszystkich osób odpowiedzialnych za tworzenie i dostarczanie oprogramowania, takich jak programiści, architekci, dyrektorzy (CTO) lub wiceprezesi (VP) ds. technicznych.

Książka skupia się na przedstawieniu wzorców architektury mikroserwisowej i innych koncepcji. Moim celem było sprawić, aby ten materiał stał się dostępny i przystępny niezależnie od używanego stosu technologicznego. Trzeba tylko znać podstawy architektury i projektowania aplikacji korporacyjnych. W szczególności należy rozumieć pojęcia, takie jak „architektura trójwarstwowa”, „projektowanie aplikacji internetowych”, „relacyjne bazy danych”, „komunikacja międzyprocesowa za pomocą przesyłania komunikatów i REST” oraz „podstawy bezpieczeństwa aplikacji”. Przykłady kodu używają Java i frameworka Spring. Aby uzyskać jak najwięcej z nich, warto zaznajomić się z frameworkiem Spring.

Mapa drogowa

Ta książka składa się z 13 rozdziałów:

■ Rozdział 1 opisuje objawy monolitycznego piekła. Występują one wówczas, gdy monolityczna aplikacja przerasta swoją architekturę. Doradza także, jak od tego uciec, przyjmując architekturę mikroserwisową. Zawiera również przegląd języka wzorców architektury mikroserwisowej, będącego motywem przewodnim znacznej części książki.

■ Rozdział 2 wyjaśnia, dlaczego architektura oprogramowania jest ważna, i opisuje wzorce, których można użyć do rozłożenia aplikacji na zbiór usług. Tłumaczy także, jak pokonać różne przeszkody, które zwykle napotykamy po drodze.

■ Rozdział 3 opisuje różne wzorce niezawodnej komunikacji międzyprocesowej w architekturze mikroserwisowej. Objaśnia, dlaczego asynchroniczna komunikacja oparta na komunikatach jest często najlepszym wyborem.

■ Rozdział 4 wyjaśnia, w jaki sposób zachować spójność danych w różnych usługach za pomocą wzorca sagi. Saga to sekwencja lokalnych transakcji koordynowanych za pomocą komunikatów asynchronicznych.

■ Rozdział 5 pokazuje, jak zaprojektować logikę biznesową dla usługi z użyciem wzorca agregatów DDD (domain-driven design) oraz zdarzeń domenowych.

■ Rozdział 6 opiera się na rozdziale 5 i wyjaśnia, w jaki sposób rozwijać logikę biznesową za pomocą skoncentrowanego na zdarzeniach wzorca event sourcingu w celu ustrukturyzowania logiki biznesowej i utrzymywania obiektów domenowych.

■ Rozdział 7 omawia sposób implementowania zapytań, które pobierają dane rozproszone w wielu usługach, z użyciem wzorca kompozycji API lub Command Query Responsibility Segregation (CQRS).

■ Rozdział 8 dotyczy wzorców zewnętrznych API do obsługi żądań ze zróżnicowanego zbioru klientów zewnętrznych, takich jak aplikacje mobilne, aplikacje JavaScript oparte na przeglądarce i aplikacje podmiotów zewnętrznych.

■ Rozdział 9 to pierwszy z dwóch rozdziałów na temat automatycznych technik testowania mikroserwisów. Wprowadza ważne koncepcje testowania, takie jak piramida testów, która opisuje względne proporcje każdego rodzaju testu w zestawie testów. Pokazuje także, jak pisać testy jednostkowe, które stanowią podstawę piramidy testów.

■ Rozdział 10 opiera się na rozdziale 9 i pokazuje, jak pisać inne typy testów w piramidzie testów, w tym testy integracyjne, testy kontraktowe i testy komponentów.

■ Rozdział 11 obejmuje różne aspekty opracowywania usług gotowych do produkcji, w tym bezpieczeństwo, wzorzec konfiguracji zewnętrznej i wzorce obserwowalności usług. Wzorce obserwowalności usług obejmują agregację dzienników, metryki aplikacji i śledzenie rozproszone.

■ Rozdział 12 opisuje różne wzorce wdrażania, których można użyć do wdrażania usług, w tym maszyny wirtualne, kontenery i wzorce bezserwerowe. Omówiono w nim także zalety korzystania z siatki usług, warstwy oprogramowania sieciowego, która pośredniczy w komunikacji w architekturze mikroserwisowej.

■ Rozdział 13 wyjaśnia, jak przyrostowo refaktoryzować architekturę monolityczną do architektury mikroserwisowej, stosując wzorzec aplikacji dusiciela: wdrażanie nowych funkcjonalności jako usług oraz wyodrębnianie modułów z monolitu i konwertowanie ich na usługi.

W miarę przechodzenia przez te rozdziały poznamy różne aspekty architektury mikroserwisowej.

O kodzie

Ta książka zawiera wiele przykładów kodu źródłowego, zarówno w ponumerowanych listingach, jak i wewnątrz zwykłego tekstu. W obu przypadkach kod źródłowy jest sformatowany w ten sposób czcionką o stałej szerokości, aby oddzielić go od zwykłego tekstu. Czasami kod jest również pogrubiony, żeby móc wskazać miejsca, w których się zmienił w porównaniu z poprzednimi krokami w danym rozdziale, na przykład gdy została dodana w nim nowa funkcjonalność. W wielu przypadkach ponownie sformatowaliśmy oryginalny kod źródłowy; dodaliśmy linie podziału i przerobiliśmy wcięcia, aby uwzględnić dostępne miejsce na stronie w książce. W rzadkich przypadkach nawet to nie było wystarczające, mimo że listingi zawierają znaczniki kontynuacji linii (➥). Ponadto często w listingach usuwaliśmy komentarze z kodu źródłowego, jeśli został on opisany w tekście. Wielu listingom towarzyszą adnotacje dotyczące kodu, podkreślające ważne pojęcia.

Każdy rozdział, z wyjątkiem rozdziałów 1, 2 i 13, zawiera kod z przykładowej aplikacji. Kod tej aplikacji można znaleźć w repozytorium GitHub: https://github.com/microservices-patterns/ftgo-application.

Forum książki

Zakup opracowania Mikroserwisy.Wzorce z przykładami w języku Java umożliwia bezpłatny dostęp do prywatnego forum internetowego prowadzonego przez Manning Publications. Można na nim zamieszczać komentarze na temat książki, zadawać pytania techniczne i otrzymywać pomoc od autorów i innych użytkowników. Aby uzyskać dostęp do forum, należy przejść do strony https://forums.manning.com/forums/microservices-patterns. Więcej o forach Manninga i zasadach korzystania można dowiedzieć się także na https://forums.manning.com/forums/about.

Zobowiązanie Manninga względem swoich czytelników polega na zapewnieniu miejsca, w którym może odbywać się konstruktywny dialog między poszczególnymi czytelnikami oraz między czytelnikami a autorami. Nie jest to zobowiązanie się do konkretnej formy uczestnictwa ze strony autorów, których wkład na forum pozostaje dobrowolny (i niepłatny). Sugerujemy zadawanie autorom wielu wymagających pytań, aby dzięki temu mogli zgłębiać temat! Forum i archiwa poprzednich dyskusji będą dostępne na stronie internetowej wydawcy tak długo, jak książka będzie publikowana.

Inne zasoby online

Innym doskonałym źródłem do nauki architektury mikroserwisowej jest moja strona http://microservices.io.

Zawiera ona nie tylko pełny język wzorców, ale także linki do innych zasobów, takich jak artykuły, prezentacje i przykładowy kod.

o autorze

Chris Richardson jest programistą i architektem. Posiada tytuły Java Champion i JavaOne Rock Star oraz jest autorem książki POJOs in Action (Manning, 2006), która opisuje, jak budować aplikacje Java z platformami, takimi jak Spring i Hibernate.

Chris jest także założycielem oryginalnej CloudFoundry.com, wczesnego Java PaaS dla Amazon EC2.

Dziś jest uznanym liderem w dziedzinie mikroserwisów i regularnie wygłasza prelekcje na międzynarodowych konferencjach. Chris jest twórcą Microservices.io, języka wzorców dla mikroserwisów. Oferuje konsultacje i szkolenia w zakresie mikroserwisów dla organizacji, które stosują architekturę mikrousługową, na całym świecie. Chris pracuje nad swoim trzecim startupem Eventuate.io, platformą aplikacyjną do opracowywania transakcyjnych mikroserwisów.

o ilustracji na okładce

Obraz na okładce książki Mikroserwisy. Wzorce z przykładami w języku Java jest zatytułowany „Habit of a Morisco Slave in 1568”. Ilustracja pochodzi z dzieła Thomasa Jefferysa A Collection of the Dresses of Different Nations, Ancient and Modern (cztery tomy), opublikowanego w Londynie w latach 1757–1772. Strona tytułowa głosi, że są to ręcznie kolorowane miedziane ryciny, wzmocnione gumą arabską.

Thomas Jefferys (1719–1771) został nazwany „Geografem Króla Jerzego III”. Był angielskim kartografem i głównym dostawcą map w swoich czasach. Wygrawerował i wydrukował mapy dla rządu i innych oficjalnych organów, a także opracował szeroki asortyment map i atlasów, zwłaszcza dla Ameryki Północnej. Jego praca jako twórcy map wzbudziła zainteresowanie lokalnymi zwyczajami ubioru na ziemiach, które zbadał i zmapował, co jest doskonale widoczne w tej kolekcji. Fascynacja odległymi krainami i podróże dla przyjemności były nowymi zjawiskami pod koniec XVIII wieku, a kolekcje takie jak te były popularne, wprowadzając zarówno turystów, jak i podróżujących palcem po mapie do mieszkańców innych krajów.

Różnorodność rysunków w dziełach Jefferysa wyraźnie pokazuje wyjątkowość i indywidualność narodów świata sprzed około 200 lat. Od tego czasu uległy zmianie sposoby ubierania się, a znaczna różnorodność w zależności od regionu i kraju, tak bogata w tamtym czasie, zanikła. Obecnie często trudno jest odróżnić mieszkańców jednego kontynentu od mieszkańców innych kontynentów. Być może, gdy spróbujemy spojrzeć na to optymistycznie, okaże się, że wymieniliśmy kulturową i wizualną różnorodność na bardziej zróżnicowane życie osobiste lub bardziej zróżnicowane i interesujące życie intelektualne i techniczne.

W czasach, gdy trudno jest odróżnić jedną książkę komputerową od innej, Manning łączy pomysłowość i inicjatywę biznesu komputerowego z okładkami opartymi na bogatej różnorodności życia regionalnego sprzed dwóch wieków, przywróconej do życia dzięki obrazom Jefferysa.

1.
Ucieczka z monolitycznego piekła

Niniejszy rozdział dotyczy

■ objawów monolitycznego piekła i sposobów na ucieczkę od niego dzięki przyjęciu architektury mikroserwisowej;

■ zasadniczych cech architektury mikroserwisowej oraz jej zalet i wad;

■ tego, jak mikroserwisy umożliwiają zastosowanie stylu DevOps do tworzenia dużych, złożonych aplikacji;

■ języka wzorców architektury mikroserwisowej i powodów, dla których należy go używać.

Był dopiero poniedziałek w porze lunchu, ale Mary, dyrektor ds. technicznych w Food to Go, Inc. (FTGO), była już sfrustrowana. Jej dzień zaczął się naprawdę dobrze. Poprzedni tydzień spędziła z innymi architektami i programistami na doskonałej konferencji, poznając najnowsze techniki tworzenia oprogramowania, w tym ciągłe wdrażanie i architekturę mikroserwisową. Mary spotkała się również ze swoimi byłymi kolegami z informatyki z North Carolina A&T State University i podzieliła się opowieściami związanymi z technologicznym przywództwem. Konferencja sprawiła, że poczuła się silna i chętna do ulepszania sposobu, w jaki FTGO rozwija oprogramowanie.

Na nieszczęście to uczucie szybko ustąpiło. Właśnie spędziła pierwszy poranek w biurze na kolejnym bolesnym spotkaniu ze starszymi inżynierami i biznesmenami. Dwie godziny poświęcili na dyskusję o tym, dlaczego zespół programistów znowu spóźni się z kolejnym krytycznym wydaniem. Niestety tego rodzaju spotkania stały się coraz powszechniejsze w ciągu ostatnich kilku lat. Mimo zastosowania oprogramowania zwinnego tempo rozwoju spadało, a więc osiągnięcie celów firmy było prawie niemożliwe. I, co gorsza, nie było prostego rozwiązania.

Konferencja uświadomiła Mary, że FTGO cierpi z powodu monolitycznego piekła i że lekarstwem byłoby przyjęcie architektury mikroserwisowej. Ale architektura mikroserwisowa i związane z nią najnowocześniejsze praktyki opracowywania oprogramowania przedstawione na konferencji wydawały się nieuchwytnym marzeniem. Dla Mary nie było jasne, jak może walczyć z dzisiejszymi pożarami, poprawiając jednocześnie sposób opracowywania oprogramowania w FTGO.

Na szczęście, jak dowiemy się z tej książki, istnieje wyjście z tej sytuacji. Ale najpierw spójrzmy na problemy, przed którymi stoi FTGO, oraz na to, z czego one wynikają.

1.1. Powolny marsz w kierunku monolitycznego piekła

Od momentu startu pod koniec 2005 r. FTGO rozwijało się błyskawicznie. Dziś jest jedną z głównych internetowych firm dostarczających żywność w Stanach Zjednoczonych. Firma planuje nawet ekspansję zagraniczną, choć plany te są zagrożone z powodu opóźnień we wdrażaniu niezbędnych funkcjonalności.

W swojej istocie aplikacja FTGO jest dość prosta. Konsumenci korzystają ze strony internetowej lub aplikacji mobilnej do składania zamówień na jedzenie w lokalnych restauracjach. FTGO koordynuje sieć kurierów, którzy dostarczają zamówienia. Odpowiada również za płacenie kurierom i restauracjom. Restauracje używają strony internetowej FTGO do edycji menu i zarządzania zamówieniami. Aplikacja korzysta z różnych usług sieciowych, w tym ze Stripe’a – do płatności, Twilio – do przesyłania wiadomości oraz Amazon Simple Email Service (SES) – do poczty e-mail.

Podobnie jak wiele innych starzejących się aplikacji korporacyjnych, aplikacja FTGO jest monolitem składającym się z pojedynczego pliku Java Web Application Archive (WAR). Z biegiem lat stała się dużą, złożoną aplikacją. Mimo wysiłków zespołu programistów stała się ona przykładem wzorca wielkiej kuli błota (www.laputan.org/mud/). Cytując Foote’a i Yodera, autorów tego wzorca, jest to „przypadkowo zbudowana, rozwlekła, niechlujna, połączona taśmą klejącą i drutem, dżungla kodu spaghetti”. Tempo dostarczania oprogramowania spowolniło. Co gorsza, aplikacja FTGO została napisana z użyciem jeszcze bardziej przestarzałych frameworków. Aplikacja FTGO wykazuje wszystkie objawy monolitycznego piekła.

Kolejny punkt opisuje architekturę aplikacji FTGO. Następnie jest mowa o tym, dlaczego monolityczna architektura początkowo działała dobrze. Zobaczymy, jak aplikacja FTGO przerosła swoją architekturę i jak doprowadziło to do monolitycznego piekła.

1.1.1. Architektura aplikacji FTGO

FTGO jest typową korporacyjną aplikacją Java. Rysunek 1.1 pokazuje jej architekturę. Aplikacja FTGO ma architekturę heksagonalną, która jest stylem architektonicznym opisanym bardziej szczegółowo w rozdziale 2. W architekturze heksagonalnej rdzeń aplikacji składa się z logiki biznesowej. Logikę biznesową otaczają różne adaptery, które implementują interfejsy użytkownika i integrują się z systemami zewnętrznymi.

Rysunek 1.1. Aplikacja FTGO ma architekturę heksagonalną. Składa się z logiki biznesowej otoczonej adapterami, które implementują interfejsy użytkownika i współpracują z systemami zewnętrznymi, takimi jak aplikacje mobilne i usługi w chmurze, w celu realizacji płatności, przesyłania wiadomości i e-maili

Logika biznesowa składa się z modułów, z których każdy jest zbiorem obiektów domenowych. Przykładami modułów są: Order Management, Delivery Management, Billing i Payments. Istnieje kilka adapterów, które współpracują z systemami zewnętrznymi. Niektóre z nich to adaptery wejściowe, które obsługują żądania, wywołując logikę biznesową. Przykładami są adaptery REST API i Web UI. Istnieją także adaptery wyjściowe, które umożliwiają logice biznesowej dostęp do bazy danych MySQL i wywoływanie usług w chmurze, takich jak Twilio i Stripe.

Mimo logicznej modułowej architektury aplikacja FTGO została spakowana jako pojedynczy plik WAR. Aplikacja jest przykładem powszechnie stosowanego monolitycznego stylu architektury oprogramowania, który tworzy system jako pojedynczy wykonywalny lub możliwy do wdrożenia komponent. Gdyby aplikacja FTGO została napisana w języku Go (GoLang), byłby to pojedynczy plik wykonywalny. Wersja aplikacji w Ruby lub NodeJS byłaby pojedynczym katalogiem z hierarchią kodu źródłowego. Monolityczna architektura nie jest z natury zła. Programiści FTGO podjęli dobrą decyzję, wybierając do swoich zastosowań architekturę monolityczną.

1.1.2. Zalety architektury monolitycznej

W początkowym okresie, gdy aplikacja FTGO była niewielka, monolityczna architektura aplikacji miała wiele zalet:

■ Prostota w rozwijaniu – IDE i inne narzędzia programistyczne koncentrują się na budowaniu pojedynczej aplikacji.

■ Łatwość we wprowadzaniu radykalnych zmian w aplikacji – można zmienić kod i schemat bazy danych, a następnie to zbudować i wdrożyć.

■ Prostota w testowaniu – programiści napisali kompleksowe testy, które uruchomiły aplikację, wywołały interfejs REST API i przetestowały interfejs użytkownika za pomocą Selenium.

■ Prostota w instalacji – programista musiał tylko skopiować plik WAR na serwer, na którym zainstalowano Tomcat.

■ Łatwość skalowania – FTGO umożliwiała uruchomianie wielu instancji aplikacji z użyciem równoważenia obciążenia.

Z czasem jednak projektowanie, testowanie, wdrażanie i skalowanie stały się znacznie trudniejsze. Zobaczmy dlaczego.

1.1.3. Życie w monolitycznym piekle

Niestety, jak mogli się przekonać twórcy FTGO, architektura monolityczna ma ogromne ograniczenia. Popularne aplikacje, takie jak FTGO, mają zwyczaj przerastać architekturę monolityczną. W każdym sprincie zespół programistów FTGO wdrażał kilka kolejnych wymagań i z tego powodu baza kodu stawała się coraz większa. Ponadto, w miarę jak firma odnosiła coraz większe sukcesy, stale rosła liczebność zespołu programistów. Nie tylko zwiększyło to tempo wzrostu bazy kodu, ale także ogólne koszty zarządzania.

Jak pokazuje rysunek 1.2, niegdyś mała, prosta aplikacja FTGO z biegiem lat przekształciła się w monstrualny monolit. Podobnie, mały zespół programistów stał się teraz wieloma zespołami scrumowymi, z których każdy pracuje w określonym obszarze funkcjonalnym. W wyniku przerośnięcia swojej architektury FTGO znajduje się w monolitycznym piekle. Rozwój jest powolny i bolesny. Zwinne opracowywanie i wdrażanie jest niemożliwe. Zobaczmy, dlaczego tak się stało.

Kompleksowość przeraża deweloperów

Głównym problemem związanym z aplikacją FTGO jest to, że jest zbyt skomplikowana. Jest za duża, aby mógł ją w pełni zrozumieć jakikolwiek programista. W rezultacie naprawianie błędów i prawidłowe wdrażanie nowych funkcjonalności stało się trudne i czasochłonne. Przekraczane są terminy.

Rysunek 1.2. Przypadek monolitycznego piekła. Duży zespół programistów FTGO dokonuje zmian w jednym repozytorium kodów źródłowych. Ścieżka od zatwierdzenia kodu do produkcji jest długa i uciążliwa oraz wymaga ręcznego testowania. Aplikacja FTGO jest duża, złożona, zawodna i trudna w utrzymaniu

Co gorsza, ta wzrastająca złożoność jest zwykle spiralą spadkową. Jeśli kod jest trudny do zrozumienia, programista może nie wprowadzić poprawnych zmian. Każda zmiana powoduje, że kod jest coraz bardziej złożony i trudniejszy do zrozumienia. Czysta, modułowa architektura pokazana wcześniej na rysunku 1.1 nie odzwierciedla rzeczywistości. FTGO stopniowo staje się potworną, niezrozumiałą, wielką kulą błota.

Mary pamięta, że niedawno uczestniczyła w pewnej konferencji. Spotkała na niej programistę, który pisał narzędzie do analizy zależności między tysiącami plików JAR w ich aplikacji składającej się z wielu milionów linii kodu. W tym czasie narzędzie to wydawało się być czymś, co FTGO mogłoby wykorzystać. Teraz już nie jest tego taka pewna. Mary sądzi, że lepszym podejściem jest migracja do architektury, która lepiej jest dostosowana do złożonej aplikacji: mikrousługi.

Rozwój jest powolny

Programiści FTGO uważają, że coraz więcej czasu, oprócz walki z przytłaczającą złożonością, zabierają codzienne zadania programistyczne. Duża aplikacja przeciąża i spowalnia środowiska projektowe deweloperów. Zbudowanie aplikacji FTGO zajmuje dużo czasu. Ponadto, ponieważ jest ona tak duża, jej uruchomienie również zajmuje sporo czasu. W rezultacie pętla edycja – budowanie – uruchamianie – testowanie zajmuje dużo czasu, co źle wpływa na wydajność.

Ścieżka od zatwierdzenia do wdrożenia jest długa i mozolna

Inny problem związany z aplikacją FTGO polega na tym, że wdrażanie zmian do produkcji jest długim i bolesnym procesem. Zespół zazwyczaj wdraża aktualizacje do produkcji raz w miesiącu, zwykle późno w piątek lub w sobotę wieczorem. Mary dowiedziała się, że obecnie cechą aplikacji oprogramowania jako usługi (Software-as-a-Service, SaaS) jest ciągłe wdrażanie: wdrażanie zmian do produkcji wiele razy dziennie w trakcie godzin pracy. Jak się okazuje, od 2011 r. Amazon.com wdraża zmiany do produkcji średnio co 11,6 sekundy, nie wpływając na użytkownika! Dla programistów FTGO aktualizacja produkcji więcej niż raz w miesiącu wydaje się odległym marzeniem. A przyjęcie ciągłego wdrażania wydaje się prawie niemożliwe.

FTGO częściowo przyjęło metodykę zwinnego zarządzania. Zespół inżynierów jest podzielony na małe zespoły i wykorzystuje dwutygodniowe sprinty. Niestety droga od kompletnego kodu do uruchomienia w produkcji jest długa i uciążliwa. Jednym z problemów związanych z tyloma programistami wprowadzającymi zmiany do tej samej bazy kodu jest to, że często on nie jest w stanie stworzyć wydania. Gdy programiści FTGO próbowali rozwiązać ten problem za pomocą gałęzi, ich próby powodowały konieczność późniejszego długiego i bolesnego scalania. W konsekwencji, gdy zespół zakończy sprint, następuje długi okres testowania i stabilizacji kodu.

Innym powodem, dlaczego tak długo trwa wprowadzanie zmian do produkcji, jest to, że dużo czasu zajmuje testowanie. Ponieważ kod jest złożony, a wpływ danej zmiany nie jest dobrze poznany, programiści i serwer wykonujący ciągłą integrację (Continuous Integration, CI) muszą uruchamiać cały pakiet testów. Niektóre części systemu wymagają nawet ręcznego testowania. Diagnoza i naprawa przyczyny niepowodzenia testu zajmuje trochę czasu. W rezultacie cykl testowania zajmuje kilka dni.

Skalowanie jest trudne

Zespół FTGO ma również problemy ze skalowaniem swojej aplikacji. Dzieje się tak dlatego, że różne moduły aplikacji mają sprzeczne wymagania dotyczące zasobów. Na przykład dane dotyczące restauracji są przechowywane w dużej bazie danych znajdującej się w pamięci, która idealnie nadaje się na serwery z dużą ilością pamięci. Moduł przetwarzania obrazu zaś wymaga dużej mocy obliczeniowej procesora i najlepiej go zainstalować na serwerach z dużą liczbą procesorów. Ponieważ te moduły są częścią tej samej aplikacji, FTGO musi iść na kompromis, jeśli chodzi o konfigurację serwera.

Dostarczanie niezawodnego monolitu jest wyzwaniem

Kolejnym problemem związanym z aplikacją FTGO jest jej awaryjność. W rezultacie występują częste przestoje w produkcji. Jednym z powodów, dlaczego tak się dzieje, jest to, że dokładne testowanie aplikacji jest trudne ze względu na jej duży rozmiar. Ten brak możliwości testowania oznacza, że trafiają błędy do produkcji. Co gorsza, aplikacji brakuje izolacji błędów, ponieważ wszystkie moduły działają w ramach tego samego procesu. Co jakiś czas błąd w jednym module – na przykład wyciek pamięci – powoduje awarię wszystkich instancji aplikacji, jedna po drugiej. Programiści FTGO nie lubią być budzeni w środku nocy z powodu awarii na produkcji. Ludziom biznesu jeszcze mniej podoba się utrata dochodów i zaufania.

Blokada w coraz bardziej przestarzałym stosie technologicznym

Ostatnim aspektem monolitycznego piekła, jakiego doświadcza zespół FTGO, jest to, że architektura zmusza ich do korzystania ze stosu technologicznego, który staje się coraz bardziej przestarzały. Architektura monolityczna utrudnia wprowadzenie nowych frameworków i języków. Przepisanie całej monolitycznej aplikacji, aby wykorzystywała nową i prawdopodobnie lepszą technologię, byłoby niezwykle kosztowne i ryzykowne. W związku z tym programiści tkwią w technologii, którą wybrali na początku projektu. Coraz częściej muszą utrzymywać aplikację napisaną za pomocą coraz bardziej przestarzałego stosu technologicznego.

Framework Spring ewoluuje, ale jednocześnie jest kompatybilny wstecz, zatem teoretycznie FTGO można by zaktualizować. Niestety aplikacja FTGO używa wersji frameworków, które są niekompatybilne z nowszymi wersjami Springa. Zespół programistów nigdy nie znalazł czasu na aktualizację tych frameworków. W rezultacie główne części aplikacji są pisane z użyciem coraz bardziej nieaktualnych narzędzi. Co więcej, programiści FTGO chcieliby eksperymentować z językami innymi niż JVM, takimi jak GoLang i NodeJS. Niestety nie jest to możliwe w przypadku aplikacji monolitycznej.

1.2. Dlaczego ta książka jest dla ciebie odpowiednia

Prawdopodobnie jesteś programistą, architektem, dyrektorem lub wiceprezesem ds. technologii. Jesteś odpowiedzialny za aplikację, która przerosła swoją monolityczną architekturę. Podobnie jak Mary w FTGO, zmagasz się z dostawami oprogramowania i chcesz wiedzieć, jak uciec od monolitycznego piekła. A może boisz się, że twoja organizacja jest na drodze do monolitycznego piekła, i chcesz wiedzieć, jak zmienić kierunek, zanim będzie za późno. Jeśli chcesz uciec lub uniknąć monolitycznego piekła, ta książka jest dla ciebie.

Ta książka poświęca wiele stron na wyjaśnienie koncepcji architektury mikroserwisowej. Moim celem jest, aby była ona dla ciebie przydatna, niezależnie od stosu technologicznego, jakiego używasz. Wystarczy zapoznać się z podstawami architektury i projektowania aplikacji korporacyjnych. W szczególności musisz znać następujące:

■ architekturę trójwarstwową;

■ projektowanie aplikacji internetowych;

■ jak rozwijać logikę biznesową za pomocą projektowania obiektowego;

■ jak korzystać z RDBMS: SQL i transakcje ACID;

■ jak korzystać z komunikacji międzyprocesowej za pomocą brokera komunikatów i interfejsów REST API;

■ zasady bezpieczeństwa, w tym uwierzytelnianie i autoryzacja.

Przykłady kodu w tej książce zostały napisane z użyciem Javy i środowiska Spring. Oznacza to, że aby w pełni wykorzystać przykłady, należy także zapoznać się z frameworkiem Spring.

1.3. Czego się nauczysz z tej książki?

Zanim skończysz czytać tę książkę, poznasz:

■ zasadnicze cechy architektury mikroserwisowej, zalety i wady oraz kiedy z niej korzystać;

■ rozproszone wzorce zarządzania danymi;

■ skuteczne strategie testowania mikroserwisów;

■ możliwości wdrażania mikroserwisów;

■ strategie przekształcenia aplikacji monolitycznej w architekturę mikroserwisową.

Będziesz także potrafił wykonać następujące czynności:

■ zbudować aplikację z użyciem wzorca architektury mikroserwisowej;

■ określić logikę biznesową dla usługi;

■ użyć sag, aby zachować spójność danych w różnych usługach;

■ zaimplementować zapytania obejmujące usługi;

■ skutecznie przetestować mikroserwis;

■ opracować usługi gotowe do produkcji, które są bezpieczne, konfigurowalne i obserwowalne;

■ przystosować istniejącą aplikację monolityczną do usług.

1.4. Architektura mikroserwisowa na ratunek

Mary doszła do wniosku, że FTGO musi zostać zmigrowane do architektury mikroserwisowej.

Co ciekawe, architektura oprogramowania ma bardzo niewiele wspólnego z wymaganiami funkcjonalnymi. Można zaimplementować zestaw przypadków użycia – wymagania funkcjonalne aplikacji – w dowolnej architekturze. W rzeczywistości typowe dla udanych aplikacji, takich jak FTGO, jest to, że są dużymi kulami błota.

Architektura ma jednak znaczenie, ponieważ wpływa na tak zwane wymagania dotyczące jakości usług, zwane również wymaganiami niefunkcjonalnymi, atrybutami jakości lub – ilities². W miarę rozwoju aplikacji FTGO ucierpiały różne atrybuty jakości, w szczególności te, które wpływają na szybkość dostarczania oprogramowania: łatwość konserwacji, rozszerzalność i testowalność.

Z jednej strony zdyscyplinowany zespół może spowolnić tempo zmierzania w kierunku monolitycznego piekła. Członkowie zespołu mogą ciężko pracować, aby zachować modułowość swojej aplikacji. Potrafią pisać kompleksowe, automatyczne testy. Z drugiej strony nie mogą uniknąć problemów dużego zespołu pracującego nad pojedynczą aplikacją monolityczną. Nie mogą też rozwiązać problemu coraz bardziej przestarzałego stosu technologicznego. Najlepsze, co może zrobić zespół, to opóźnić nieuniknione. Aby uciec od monolitycznego piekła, muszą przeprowadzić migrację do nowej architektury: architektury mikroserwisowej.

Obecnie panuje konsensus, że jeśli budujemy dużą, złożoną aplikację, powinniśmy rozważyć użycie architektury mikroserwisowej. Ale czym dokładnie są mikrousługi? Niestety nazwa nie pomaga, ponieważ zbyt podkreśla rozmiar. Istnieje wiele definicji architektury mikroserwisowej. Niektórzy przyjmują tę nazwę zbyt dosłownie i twierdzą, że usługa powinna być niewielka – na przykład 100 LOC. Inni twierdzą, że opracowanie usługi powinno zająć tylko dwa tygodnie. Adrian Cockcroft, kiedyś pracujący w Netflix, definiuje architekturę mikroserwisową jako architekturę zorientowaną na usługi, złożoną z luźno powiązanych ze sobą elementów, które mają ograniczone konteksty. To nie jest zła definicja, ale trochę mętna. Zobaczmy, czy możemy to lepiej określić.

1.4.1. Sześcian skal i mikroserwisy

Moja definicja architektury mikroserwisowej jest inspirowana doskonałą pracą Martina Abbotta i Michaela Fishera The Art of Scalability (Addison-Wesley, 2015). Książka ta opisuje użyteczny trójwymiarowy model skalowalności – sześcian skal – pokazany na rysunku 1.3.

Rysunek 1.3. Sześcian skal definiuje trzy oddzielne sposoby skalowania aplikacji: skalowanie w osi X równoważy obciążenia związane z żądaniami między wiele identycznych instancji; skalowanie w osi Z trasuje żądania na podstawie atrybutu żądania; skalowanie w osi Y funkcjonalnie rozkłada aplikację na usługi

Model definiuje trzy drogi skalowania aplikacji: X, Y i Z.

Skalowanie w osi X równoważy obciążenia dotyczące żądań między wiele instancji

Skalowanie w osi X jest powszechnym sposobem skalowania aplikacji monolitycznej. Rysunek 1.4 pokazuje, jak działa skalowanie w osi X. Uruchamiamy wiele instancji aplikacji za modułem równoważenia obciążenia. Moduł równoważenia obciążenia rozdziela żądania między N identycznych instancji aplikacji. Jest to świetny sposób na poprawę wydajności i dostępności aplikacji.

Skalowanie w osi Z trasuje żądania na podstawie atrybutu żądania

Skalowanie w osi Z uruchamia również wiele instancji aplikacji monolitycznej, ale w przeciwieństwie do skalowania w osi X każda instancja jest odpowiedzialna tylko za podzbiór danych. Rysunek 1.5 pokazuje, jak działa skalowanie w osi Z. Router przed instancjami używa atrybutu żądania, aby skierować go do odpowiedniej instancji. Aplikacja może na przykład kierować żądania na podstawie userId.

W tym przykładzie każda instancja aplikacji jest odpowiedzialna za podzbiór użytkowników. Router używa userId określonego w nagłówku Authorization żądania, aby wybrać jedno z N identycznych wystąpień aplikacji. Skalowanie w osi Z to świetny sposób na skalowanie aplikacji w celu obsługi rosnącej liczby transakcji i ilości danych.

Rysunek 1.4. Skalowanie w osi X za modułem równoważenia obciążenia uruchamia wiele identycznych instancji aplikacji monolitycznej

Rysunek 1.5. Skalowanie w osi Z uruchamia wiele identycznych instancji aplikacji monolitycznej za routerem, który kieruje żądania na podstawie atrybutu request. Każda instancja odpowiada za podzbiór danych

Skalowanie w osi Y funkcjonalnie rozkłada aplikację na usługi

Skalowanie w osi X i Z poprawia wydajność i dostępność aplikacji. Ale żadne z tych rozwiązań nie rozwiązuje problemu zwiększania złożoności programowania i aplikacji. Aby je rozwiązać, musimy zastosować skalowanie w osi Y lub rozkład funkcjonalny. Rysunek 1.6 pokazuje, jak działa skalowanie w osi Y: podzielenie monolitycznej aplikacji na zestaw usług.

Rysunek 1.6. Skalowanie w osi Y dzieli aplikację na zestaw usług. Każda usługa odpowiada za określoną funkcjonalność. Usługa jest skalowana w osi X i ewentualnie skalowania w osi Z

Usługa to miniaplikacja, która implementuje w nieznacznym zakresie ukierunkowaną funkcjonalność, taką jak zarządzanie zamówieniami, zarządzanie klientami itd. Usługa jest skalowana za pomocą skalowania w osi X, chociaż niektóre usługi mogą również wykorzystywać skalowanie w osi Z. Na przykład Usługa zarządzania zamówieniem składa się z zestawu wystąpień usługi wraz z równoważeniem obciążenia.

Wysokopoziomowa definicja architektury mikroserwisowej (mikroserwisów) określa to jako styl architektoniczny, który funkcjonalnie rozkłada aplikację na zestaw usług. Pamiętajmy, że ta definicja nie mówi nic o ich rozmiarze. Liczy się zaś to, że każda usługa ma skoncentrowany, spójny zestaw obowiązków. W dalszej części książki omówimy, co to oznacza.

Zobaczmy teraz, w jaki sposób architektura mikroserwisowa jest formą modułowości.

1.4.2. Mikroserwisy jako forma modułowości

Modułowość jest niezbędna do tworzenia dużych, złożonych aplikacji. Nowoczesna aplikacja, taka jak FTGO, jest zbyt duża, aby mogła ją opracować jedna osoba. Jest też zbyt skomplikowana, aby mogła ją zrozumieć jedna osoba. Aplikacje należy rozłożyć na moduły, które mogą być rozwijane i które mogą być rozumiane przez różne osoby. W aplikacji monolitycznej moduły są definiowane z użyciem kombinacji konstrukcji języka programowania (takich jak pakiety Java) i artefaktów kompilacji (takich jak pliki Java JAR). Niemniej jednak takie podejście, jak odkryli programiści FTGO, zwykle nie działa dobrze w praktyce. Długowieczne, monolityczne aplikacje zwykle degenerują się w duże kule błota.

Architektura mikroserwisowa wykorzystuje usługi jako jednostki modułowości. Usługa posiada API, które stanowi nieprzepuszczalną, trudną do naruszenia granicę. Nie można ominąć API i uzyskać dostęp do wewnętrznej klasy, tak jak jest to możliwe w przypadku pakietów Javy. W rezultacie w trakcie upływu czasu o wiele łatwiej jest zachować modułowość aplikacji. Istnieją także inne zalety korzystania z usług jako elementów składowych, w tym możliwość ich niezależnego wdrażania i skalowania.

1.4.3. Każda usługa ma własną bazę danych

Kluczową cechą architektury mikroserwisowej jest to, że usługi są luźno połączone i komunikują się tylko za pośrednictwem API. Jednym ze sposobów uzyskania luźnego połączenia jest to, żeby każda usługa posiadała własny magazyn danych. Na przykład w sklepie internetowym Order Service ma bazę danych, która zawiera tabelę ORDERS, Customer Service zaś ma bazę danych, która zawiera tabelę CUSTOMERS. Podczas programowania deweloperzy mogą zmieniać schemat bazy usługi bez konieczności koordynacji tego z programistami pracującymi nad innymi usługami. W trakcie wykonywania usługi są odizolowane od siebie – na przykład dana usługa nigdy nie zostanie zablokowana z tego powodu, iż inna usługa założyła blokadę na bazę danych.

Nie martwmy się: luźne powiązania nie wzbogacają Larry’ego Ellisona

Wymaganie, aby każda usługa miała własną bazę danych, nie oznacza, że ma ona własny serwer bazy danych. Wcale nie musimy z tego powodu wydawać 10 razy więcej na licencje Oracle RDBMS. Rozdział 2 szczegółowo omawia ten temat.

Teraz, gdy już zdefiniowaliśmy architekturę mikroserwisową i opisaliśmy niektóre z jej podstawowych cech, przyjrzyjmy się temu, jak odnosi się to do aplikacji FTGO.

1.4.4. Architektura mikroserwisowa FTGO

Pozostała część tej książki szczegółowo omawia architekturę mikroserwisową aplikacji FTGO. Ale najpierw przyjrzyjmy się, co to znaczy zastosować skalowanie w osi Y dla tej aplikacji. Jeśli do aplikacji FTGO zastosujemy dekompozycję w osi Y, otrzymamy architekturę taką, jaka została pokazana na rysunku 1.7. Zdekomponowana aplikacja składa się z wielu usług frontendowych i backendowych. Moglibyśmy również zastosować skalowanie w osi X i ewentualnie skalowanie w osi Z, aby w czasie jego wykonywania istniało wiele instancji każdej usługi.

Usługi frontendowe obejmują bramę API i webowy interfejs użytkownika Restauracje. Brama API, pełniąca rolę fasady i szczegółowo opisana w rozdziale 8, oferuje REST API, z których korzystają aplikacje mobilne konsumentów i kurierów. Webowy interfejs Restauracje zawiera interfejs używany przez restauracje do zarządzania menu i przetwarzania zamówień.

Logika biznesowa aplikacji FTGO obejmuje wiele usług backendowych. Każda usługa backendowa ma REST API i własny prywatny magazyn danych. Usługami backendowymi są m.in.:

■ Order Service – zarządza zamówieniami;

■ Delivery Service – zarządza dostawą zamówień z restauracji do konsumentów;

■ Restaurant Service – przechowuje informacje o restauracjach;

■ Kitchen Service – zarządza przygotowywaniem zamówień;

■ Accounting Service – obsługuje fakturowanie i płatności.

Rysunek 1.7. Niektóre usługi aplikacji FTGO opartej na architekturze mikroserwisowej. Brama API kieruje żądania z aplikacji mobilnych do usług. Usługi współpracują za pośrednictwem API

Wiele usług odpowiada modułom opisanym wcześniej w tym rozdziale. Różnica polega na tym, że każda usługa i jej API są bardzo jasno zdefiniowane. Każdą z nich można niezależnie rozwijać, testować, wdrażać i skalować. Ponadto ta architektura dobrze wspiera modułowość. Programista nie może ominąć API usługi i uzyskać dostęp do jej wewnętrznych komponentów. Rozdział 13 opisuje, jak przekształcić istniejącą aplikację monolityczną w mikroserwisy.

1.4.5. Porównanie architektury mikroserwisowej i SOA

Niektórzy krytycy architektury mikroserwisowej twierdzą, że nie jest ona niczym nowym – to architektura zorientowana na usługi (SOA). Na bardzo wysokim poziomie istnieją między nimi pewne podobieństwa. Architektura SOA i architektura mikroserwisowa to style architektoniczne, które tworzą system jako zestaw usług. Ale jak pokazuje tabela 1.1, po głębszej analizie napotkamy znaczne różnice.

Tabela 1.1. Porównanie SOA z mikroserwisami

SOA

Mikroserwisy

Typowa usługa

Większa monolityczna aplikacja

Mniejsza usługa

Komunikacja między usługami

Inteligentne potoki, takie jak Enterprise Service Bus, wykorzystujące ciężkie protokoły, takie jak SOAP i inne standardy WS*

Proste potoki, takie jak broker komunikatów lub bezpośrednia komunikacja między usługami, za pomocą lekkich protokołów, takich jak REST lub gRPC

Dane

Globalny model danych i wspólne bazy danych

Model danych i baza danych dla każdej usługi

Architektura SOA i architektura mikroserwisowa zwykle posługują się różnymi stosami technologicznymi. Aplikacje SOA zazwyczaj wykorzystują „ciężkie” technologie, takie jak SOAP i inne standardy WS*. W celu integrowania usług często używają ESB, inteligentnego potoku zawierającego logikę biznesową i przetwarzanie komunikatów. Aplikacje zbudowane z wykorzystaniem architektury mikroserwisowej zwykle używają lekkich technologii open source. Usługi komunikują się za pomocą prostych potoków, takich jak brokerzy komunikatów lub lekkich protokołów, np. REST lub gRPC.

Architektura SOA i architektura mikroserwisowa różnią się także sposobem traktowania danych. Aplikacje SOA mają zazwyczaj globalny model danych i współużytkują bazy danych. Z kolei w architekturze mikroserwisowej, jak wspomniano wcześniej, każda usługa ma własną bazę danych. Ponadto, jak opisano w rozdziale 2, zwykle przyjmuje się, że każda usługa ma własny model domeny.

Inną kluczową różnicą między architekturą SOA a architekturą mikroserwisową jest wielkość usług. SOA jest zwykle używana do integracji dużych, złożonych monolitycznych aplikacji. Chociaż usługi w architekturze mikroserwisowej nie zawsze są miniaturowe, to prawie zawsze są znacznie mniejsze niż w SOA. W rezultacie aplikacja SOA zwykle składa się z kilku dużych usług, podczas gdy aplikacja oparta na mikroserwisach zwykle składa się z kilkudziesięciu lub setek mniejszych usług.

1.5. Zalety i wady architektury mikroserwisowej

Rozważmy najpierw korzyści, a następnie przyjrzymy się wadom.

1.5.1. Zalety architektury mikroserwisowej

Architektura mikroserwisowa ma następujące zalety:

■ Umożliwia ciągłe dostarczanie i wdrażanie dużych, złożonych aplikacji.

■ Usługi są małe i łatwe w utrzymaniu.

■ Usługi można wdrażać niezależnie.

■ Usługi są niezależnie od siebie skalowalne.

■ Pozwala zespołom na autonomiczność.

■ Umożliwia łatwe eksperymentowanie i wdrażanie nowych technologii.

■ Ma lepszą izolację błędów.

Spójrzmy na każdą zaletę.

Umożliwianie ciągłego dostarczania i wdrażania dużych, złożonych aplikacji

Najważniejszą zaletą architektury mikroserwisowej jest to, że umożliwia ona ciągłe dostarczanie i wdrażanie dużych, złożonych aplikacji. Jak opisano w dalszej części, w podrozdziale 1.7, ciągłe dostarczanie/wdrażanie jest częścią DevOps, zestawu praktyk dotyczącego szybkiego, częstego i niezawodnego dostarczania oprogramowania. Wysoko wydajne organizacje DevOps zazwyczaj wdrażają zmiany na produkcji z bardzo niewielką liczbą problemów.

Architektura mikroserwisowa umożliwia ciągłe dostarczanie/wdrażanie na trzy sposoby:

■ Oferuje testowalność wymaganą w czasie ciągłego dostarczania/wdrażania – zautomatyzowane testowanie jest kluczową praktyką ciągłego dostarczania/wdrażania. Ponieważ każda usługa w architekturze mikroserwisowej jest niewielka, automatyczne testy są znacznie łatwiejsze do napisania i szybsze do wykonania. W rezultacie aplikacja będzie miała mniej błędów.

■ Oferuje możliwości wdrażania wymagane w trakcie ciągłego dostarczania/wdrażania – każdą usługę można wdrożyć niezależnie od innych usług. Jeśli programiści odpowiedzialni za usługę muszą wdrożyć lokalną zmianę w danej usłudze, nie muszą koordynować tego z innymi programistami. Mogą wdrożyć swoje zmiany. W rezultacie znacznie łatwiej jest często wdrażać zmiany w środowisku produkcyjnym.

■ Pozwala zespołom programistycznym na autonomiczność i luźne powiązania – możemy zbudować organizację jako zbiór małych (na przykład dwupizzowych) zespołów. Każdy zespół ponosi wyłączną odpowiedzialność za rozwój i wdrażanie jednej lub więcej powiązanych usług. Jak pokazuje rysunek 1.8, każdy zespół może rozwijać, wdrażać i skalować swoje usługi niezależnie od wszystkich innych zespołów. W rezultacie prędkość rozwoju jest znacznie większa.

Z możliwością ciągłego dostarczania i wdrażania wiąże się kilka korzyści biznesowych:

■ Skrócenie czasu wprowadzania na rynek, co pozwala firmie szybko reagować na opinie klientów.

■ Umożliwia firmie zapewnienie niezawodnej usługi, której oczekują dziś klienci.

■ Większe zadowolenie pracowników, ponieważ więcej czasu poświęca się na dostarczanie cennych funkcjonalności, zamiast na walkę z pożarami.

W rezultacie architektura mikroserwisowa stała się bardzo atrakcyjna dla każdej firmy zależnej od technologii oprogramowania.

Każda usługa jest mała i łatwa w utrzymaniu

Kolejną zaletą architektury mikroserwisowej jest to, że każda usługa jest niewielka. Kod jest łatwiejszy do zrozumienia dla programisty. Mała baza kodu nie spowalnia IDE, zwiększając produktywność programistów. Każda usługa zazwyczaj uruchamia się znacznie szybciej niż duży monolit, co zwiększa produktywność programistów i przyspiesza wdrażanie.

Rysunek 1.8. Aplikacja FTGO oparta na mikroserwisach składa się ze zbioru luźno powiązanych usług. Każdy zespół niezależnie opracowuje, testuje i wdraża swoje usługi

Usługi są niezależnie skalowalne

Każda usługa w architekturze mikroserwisowej może być skalowana niezależnie od innych usług z wykorzystaniem klonowania w osi X i partycjonowania w osi Z. Ponadto każdą usługę można wdrożyć na sprzęcie najlepiej dostosowanym do jej wymagań dotyczących zasobów. Jest to zupełnie inne niż w przypadku architektury monolitycznej, w której komponenty o bardzo różnych wymaganiach dotyczących zasobów – na przykład intensywnie wykorzystujące procesor lub wymagające dużej ilości pamięci – muszą być wdrażane razem.

Lepsza izolacja błędów

Architektura mikroserwisowa ma lepszą izolację błędów. Na przykład wyciek pamięci w jednej usłudze wpływa tylko na tę usługę. Inne usługi będą nadal normalnie obsługiwać żądania. Dla porównania jeden źle zachowujący się element architektury monolitycznej zniszczy cały system.

Łatwe eksperymentowanie i wdrażanie nowych technologii

Ostatnie, ale nie mniej ważne, architektura mikroserwisowa eliminuje wszelkie długoterminowe przywiązania do stosu technologicznego. Zasadniczo programiści, opracowując nową usługę, mogą wybrać dowolny język i frameworki, które najlepiej do niej pasują. W wielu organizacjach sensowne jest ograniczenie wyboru, ale najważniejsze jest to, że nie ograniczają nas wcześniejsze decyzje.

Ponadto, ponieważ usługi są małe, przepisywanie ich z użyciem lepszych języków i technologii staje się praktyczne. Jeśli nie powiedzie się próba nowej technologii, możemy odrzucić wykonaną pracę, nie ryzykując całego projektu. Jest to zupełnie inne niż w przypadku architektury monolitycznej, w której początkowe wybory technologiczne poważnie ograniczają możliwość korzystania z różnych języków i struktur w przyszłości.

1.5.2. Wady architektury mikroserwisowej

Z pewnością żadna technologia nie jest złotym środkiem, zatem i architektura mikroserwisowa ma wiele istotnych wad i problemów. Większa część tej książki dotyczy tego, jak rozwiązywać te wady i problemy. Nie martwmy się, gdy będziemy czytać o wyzwaniach. W dalszej części tej książki opiszę sposoby ich rozwiązania.

Oto główne wady i problemy związane z architekturą mikroserwisową:

■ Znalezienie odpowiedniego zestawu usług jest trudne.

■ Systemy rozproszone są złożone, co utrudnia opracowywanie, testowanie i wdrażanie.

■ Wdrażanie funkcjonalności obejmujących wiele usług wymaga starannej koordynacji.

■ Decyzja o przyjęciu architektury mikroserwisowej jest trudna.

Spójrzmy kolejno na każde z powyższych.

Znalezienie odpowiednich usług jest trudne

Jednym z wyzwań związanych ze stosowaniem architektury mikroserwisowej jest to, że nie ma konkretnego, dobrze zdefiniowanego algorytmu do rozkładania systemu na usługi. Podobnie jak w przypadku większości programów, jest to coś w rodzaju sztuki. Co gorsza, jeśli niepoprawnie rozłożymy system, zbudujemy rozproszony monolit, system składający się z połączonych usług, które muszą zostać wdrożone razem. Rozproszony monolit ma wady zarówno architektury monolitycznej, jak i architektury mikroserwisowej.

Systemy rozproszone są złożone

Innym problemem związanym z korzystaniem z architektury mikroserwisowej jest to, że programiści muszą poradzić sobie z dodatkową złożonością tworzenia systemu rozproszonego. Usługi muszą korzystać z mechanizmu komunikacji międzyprocesowej. Jest to bardziej złożone niż proste wywołanie metody. Ponadto usługa musi być zaprojektowana w taki sposób, aby mogła obsługiwać częściowe awarie i radzić sobie z usługą zdalną, która jest niedostępna lub która wykazuje duże opóźnienia.

Wdrażanie przypadków użycia obejmujących wiele usług wymaga użycia nowych technik. Każda usługa ma własną bazę danych, co utrudnia wdrożenie transakcji i zapytań obejmujących usługi. Jak opisano w rozdziale 4, aplikacja oparta na mikroserwisach musi używać tak zwanych sag, aby zachować spójność danych w różnych usługach. Rozdział 7 wyjaśnia, że aplikacja oparta na mikroserwisach nie może pobierać danych z wielu usług za pomocą prostych zapytań. Zamiast tego musi implementować zapytania z wykorzystaniem kompozycji API lub widoków CQRS.

IDE i inne narzędzia programistyczne koncentrują się na budowaniu aplikacji monolitycznych i nie zapewniają wyraźnego wsparcia w tworzeniu aplikacji rozproszonych. Pisanie automatycznych testów obejmujących wiele usług jest trudne. Są to problemy specyficzne dla architektury mikroserwisowej. W związku z tym programiści w naszej organizacji muszą mieć zaawansowane umiejętności tworzenia i dostarczania oprogramowania, aby skutecznie korzystać z mikroserwisów.

Architektura mikroserwisowa wprowadza również znaczną złożoność operacyjną. Na produkcji musi być zarządzanych znacznie więcej wymienialnych elementów – wiele wystąpień różnych rodzajów usług. Aby pomyślnie wdrożyć mikrousługi, potrzebujemy automatyzacji na wysokim poziomie. Musimy użyć następujących technologii:

■ zautomatyzowanych narzędzi do wdrażania, np. Netflix Spinnaker;

■ gotowego PaaS, takich jak Pivotal Cloud Foundry lub Red Hat OpenShift;

■ platformy orkiestracyjnej Docker, takich jak Docker Swarm lub Kubernetes.

W rozdziale 12 przedstawię bardziej szczegółowo możliwości związane z wdrażaniem.

Wdrażanie funkcjonalności obejmujących wiele usług wymaga starannej koordynacji

Kolejnym wyzwaniem związanym z korzystaniem z architektury mikroserwisowej jest to, że wdrażanie funkcjonalności obejmujących wiele usług wymaga starannej koordynacji między różnymi zespołami programistycznymi. Musimy utworzyć plan wdrożenia, który uporządkuje wdrożenia usług na podstawie zależności między nimi. Jest to zupełnie inne niż architektura monolityczna, w której można łatwo wdrożyć aktualizacje wielu komponentów atomowo.

Decyzja o przyjęciu jest trudna

Innym problemem związanym z korzystaniem z architektury mikroserwisowej jest decyzja, w którym momencie cyklu życia aplikacji należy użyć tej architektury. Podczas opracowywania pierwszej wersji aplikacji często nie napotykamy na problemy, które rozwiązuje ta architektura. Ponadto zastosowanie skomplikowanej, rozproszonej architektury spowalnia rozwój. Może to stanowić poważny dylemat dla startupów, w których największym problemem jest zazwyczaj szybka ewolucja modelu biznesowego i aplikacji towarzyszącej. Korzystanie z architektury mikroserwisowej znacznie utrudnia szybką iterację. Startup prawie na pewno powinien rozpocząć się od aplikacji monolitycznej.

Później jednak, gdy problemem jest radzenie sobie ze złożonością, sensowne jest funkcjonalne rozłożenie aplikacji na zestaw mikroserwisów. Przekształcenie może być trudne z powodu zagmatwanych zależności. Rozdział 13 omawia strategie przekształcenia aplikacji monolitycznej do mikroserwisów.

Jak widać, architektura mikroserwisowa oferuje wiele korzyści, ale ma również pewne istotne wady. Z powodu tych problemów przyjęcie architektury mikroserwisowej nie powinno być podejmowane pochopnie. Ale w przypadku złożonych aplikacji, takich jak aplikacja internetowa lub SaaS, jest to zwykle właściwy wybór. Dobrze znane strony, takie jak eBay (www.slideshare.net/RandyShoup/the-ebay-architecture-striking-abalance-between-site-stability-feature-velocity-performance-and-cost), Amazon.com, Groupon i Gilt, ewoluowały od architektury monolitycznej do architektury mikroserwisowej.

Podczas korzystania z architektury mikroserwisowej należy rozwiązać wiele problemów projektowych i architektonicznych. Co więcej, wiele z tych problemów ma wiele rozwiązań, każde z innym zestawem kompromisów. Nie ma jednego idealnego rozwiązania. Aby pomóc w podjęciu decyzji, stworzyłem język wzorców architektury mikroserwisowej. Będę odwoływał się do tego języka wzorców w pozostałej części książki podczas nauki architektury mikroserwisowej. Zobaczmy, czym jest język wzorców i dlaczego jest pomocny.

1.6. Język wzorców architektury mikroserwisowej

Architektura i projektowanie polegają na podejmowaniu decyzji. Musimy zdecydować, czy do naszej aplikacji lepiej pasuje architektura monolityczna, czy mikroserwisowa. Podejmując tę decyzję, musimy wziąć pod uwagę wiele kompromisów. Jeśli wybierzemy architekturę mikroserwisową, musimy rozwiązać wiele problemów.

Dobrym sposobem na opisanie różnych opcji architektonicznych i projektowych oraz usprawnienie procesu decyzyjnego jest użycie języka wzorców. Przyjrzyjmy się najpierw, dlaczego potrzebujemy wzorców i języka wzorców, a następnie zapoznamy się z językiem wzorców architektury mikroserwisowej.

1.6.1. Architektura mikroserwisowa nie jest złotym środkiem

W 1986 r. Fred Brooks, autor The Mythical Man-Month (Addison-Wesley Professional, 1995), powiedział, że w inżynierii oprogramowania nie ma złotych środków. Oznacza to, że nie ma żadnych technik ani technologii, które zwiększyłyby, gdyby zostały zastosowane, wielokrotnie wydajność. Jednak kilkadziesiąt lat później programiści wciąż kłócą się o swoje ulubione złote środki, będąc absolutnie przekonani, że ich ulubiona technologia zapewni im znaczny wzrost wydajności.

Wiele argumentacji jest związanych z dwudzielnością do bani/czadowe (http://nealford.com/memeagora/2009/08/05/suck-rock-dichotomy.html), terminem ukutym przez Neala Forda, który opisuje, jak wszystko w świecie oprogramowania jest albo do bani, albo czadowe, bez żadnych kompromisów. Argumenty te mają następującą strukturę: jeśli wykonasz X, to wtedy twój pupil zdechnie, więc musisz zrobić Y. Na przykład programowanie synchroniczne kontra reaktywne, obiektowe kontra funkcjonalne, Java kontra JavaScript, REST kontra komunikaty. Oczywiście w rzeczywistości jest znacznie więcej niuansów. Każda technologia ma wady i ograniczenia, które są często pomijane przez jej zwolenników. W rezultacie przyjęcie technologii zwykle przebiega zgodnie z cyklem szumu Gartnera (https://en.wikipedia.org/wiki/Hype_cycle), w którym powstająca technologia przechodzi przez pięć faz, w tym szczyt zawyżonych oczekiwań (czadowe), następnie małe rozczarowanie (to jest do bani) i kończące się płaskowyżem produktywności (teraz rozumiemy kompromisy i wiemy, kiedy z nich korzystać).

Mikroserwisy nie są odporne na zjawisko złotego środka. To, czy ta architektura jest odpowiednia dla naszej aplikacji, zależy od wielu czynników. W związku z tym doradzanie, aby zawsze korzystać z architektury mikroserwisowej, jest złe, ale równie złe jest doradzanie, aby nigdy jej nie używać. Tak jak w przypadku wielu rzeczy, to zależy.

Podstawowym powodem tych spolaryzowanych i przesadzonych argumentów na temat technologii jest to, że ludzie kierują się przede wszystkim swoimi emocjami. Jonathan Haidt w swojej doskonałej książce The Righteous Mind: Why Good People Divided by Politics and Religion (Vintage, 2013) posługuje się metaforą słonia i jego jeźdźca, aby opisać, jak działa ludzki umysł. Słoń reprezentuje część emocjonalną ludzkiego mózgu. Podejmuje większość decyzji. Jeździec reprezentuje racjonalną część mózgu. Czasami może wpływać na słonia, ale przede wszystkim zapewnia uzasadnienie decyzji słonia.

My, społeczność programistów, musimy przezwyciężyć naszą emocjonalną naturę i znaleźć lepszy sposób na dyskutowanie i stosowanie technologii. Świetnym sposobem na dyskutowanie i opisywanie technologii jest użycie formatu wzorca, ponieważ jest on obiektywny. Opisując technologię w postaci wzorca, należy na przykład opisać wady. Rzućmy okiem na postać wzorca.

Przypisy

¹ Manning Early Access Program (MEAP) – https://www.manning.com/meap-program.

² Od wspólnego przyrostka różnych pożądanych cech, np. testability (testowalność), reliability (niezawodność).