Technologia stojąca za Ordering Stack
Główna koncepcja
Ordering Stack to platforma e-commerce i pos dla sieci restauracji.
Składa się ona z usług backendowych oraz aplikacji frontendowych. Istnieją usługi uwierzytelniania, produktów i menu, miejsc, płatności, zamówień i inne, dostarczające funkcjonalności biznesowe dla poszczególnych domen biznesowych systemu. Każda z nich eksponuje swoje własne REST API. Aby uzyskać dostęp do którejkolwiek z tych usług, klienci muszą najpierw uzyskać tokeny dostępu z usługi uwierzytelniajacych. Mając taki token, klienci mogą wywoływać punkty końcowe API podając dane uwierzytelniające w nagłówku żądania.
Oprócz wywoływania API, klienci będą otrzymywać asynchroniczne wiadomości z backendu poprzez websockets. Wiadomości te zawierają stan zamówień i inne powiadomienia.
Ogólna idea pracy z backendem polega na wywoływaniu metod API i odbieraniu aktualizacji stanu przez websocket. Aplikacje klienckie muszą być przygotowane do pracy asynchronicznej.
Przegląd Architektury
- nieblokujący i asynchroniczny
polecenia użytkownika i aktualizacje są przesyłane dwoma niezależnymi kanałami HTTP Request i WebSokets
- działa jak gry online dla wielu graczy (MMOG)
cały stan jest utrzymywany po stronie serwera
użytkownicy wysyłają komendy do serwera bez czekania na wykonanie akcji (otrzymują tylko potwierdzenie pomyślnego umieszczenia w kolejce)
zaktualizowany stan jest dostarczany asynchronicznie
Opis użycia API i websocket można znaleźć tutaj: https://docs.orderingstack.com/api-examples/
Obecnie wszystkie nasze aplikacje klienckie są realizowane jako aplikacje React.js.
Jak robiony jest backend
Podczas dyskusji nad architekturą Ordering Stack chcieliśmy kierować się zasadami DDD (Domain Driven Design) i zamykać poszczególne obszary biznesowe w osobnych (mikro)usługach. Każda usługa w ramach domeny powinna być również luźno sprzężona z innymi usługami, nie wywołując zbyt często ich API w zakresie pobierania danych spoza ograniczonego kontekstu.
Kolejną rzeczą było zaimplementowanie asynchronicznego procesu przepływu zamówień z przychodzącymi komendami z API zamówień i odsyłaniem powiadomień przez websockety. Musieliśmy choreografować wiele usług w miarę wykonywania kolejnych etapów przetwarzania zamówienia. Do tego celu wykorzystaliśmy platformę event streamingową.
Można to sobie wyobrazić w ten sposób:
Należy pamiętać, że platforma brokera komunikatów (event streaming) jest centralnym szkieletem całego systemu. Po pierwsze pomaga ona w wymianie danych pomiędzy usługami, co pozwala uniknąć zbyt dużej interakcji pomiędzy nimi. Każdy mikroserwis może utrzymywać własne dane, w tym te należące do innych serwisów i aktualizować je w miarę napływu odpowiednich zdarzeń. Po drugie, umożliwia choreografię usług, gdyż mogą one reagować na przychodzące zdarzenia i publikować inne zdarzenia w celu aktywowania innych usług. Takie podejście sprawia, że całe rozwiązanie jest znacznie bardziej zwinne, wydajne (nieblokujące), odporne na awarie i wytrzymałe. Jako przykład korzyści, mogliśmy w prosty sposób dodać wsparcie dla webhooków w procesie przetwarzania zamówień.
Z punktu widzenia implementacji wykorzystaliśmy następujące technologie:
- mikroserwisy budowane są przy użyciu Javy, Spring boot lub node.js,
- mongodb jest używany, jeśli usługa potrzebuje przechowywania danych (usługi nie mogą współdzielić baz danych).
- mikroserwis używa wykrywania usług do wyszukiwania innych usług (Eureka)
- każda mikrousługa jest uruchomiona w kontenerze Docker (docker compose używany do orkiestracji kontenerów)
- Apache Kafka jest wykorzystywana jako platforma do strumieniowania zdarzeń
Wielodostępność
Ordering Stack jest platformą SaaS w chmurze i może być wykorzystywany przez wielu klientów (najemców). Każda mikrousługa w systemie obsługuje multi-tenancy. Identyfikatory najemców są również zawsze zakodowane w tokenie dostępu. Jeśli usługa posiada swój własny magazyn (storage), to dane dla każdego najemcy są przechowywane w różnych kolekcjach. Również każdy najemca ma inne tematy do przetwarzania zleceń w platformie strumieniowania zdarzeń (Kafka).
Wydajność
Zamawianie żywności ma swoją własną charakterystykę wzorców użycia w ciągu tygodnia i godzin w ciągu dnia. Zazwyczaj występują szczyty zamówień w porze lunchu i wieczorami. [Poniedziałek jest zazwyczaj słaby, a piątek, sobota i niedziela są najsilniejsze. Dodatkowo warunki pogodowe mogą silnie wpływać na zamówienia w ciągu dnia, ulewny deszcz lub śnieg mogą znacząco zwiększyć liczbę zamówień. Wreszcie – wydarzenia marketingowe, takie jak precyzyjne reklamy czasowe lub specjalne dni, takie jak “międzynarodowy dzień pizzy” mogą odgrywać ogromną rolę w pozyskiwaniu nowych zamówień.
Biorąc pod uwagę wszystko powyższe, platforma do zamawiania musi być przygotowana na duże obciążenie. Dlatego projektując Ordering Stack postawiliśmy wydajność i skalowalność wysoko na liście naszych priorytetów. Chcieliśmy uniknąć jakichkolwiek menu i skomplikowanych obliczeń produktów podczas przetwarzania żądania, blokowania odpowiedzi z powodu przeliczania rabatów w koszyku i tak dalej. Tworząc ten system, niektóre logiki mogą być umieszczone poza rdzeniem systemu, jak webhooks, które mogą być przypisane swobodnie przez najemcę w jego konfiguracji. Nie mamy żadnej kontroli nad wydajnością takich elementów! Odpowiedzią jest asynchroniczność. Wszystkie akcje związane z zamówieniami jak np. dodanie produktu do koszyka wysyłane są do backendu jako komendy poprzez żądanie REST. Są one umieszczane w strumieniu kafka do dalszego przetwarzania przez wiele mikroserwisów w różnych domenach, takich jak weryfikacja menu, przetwarzanie zamówień, rabaty, lojalność itp. i kiedy nowy stan zamówienia jest gotowy, jest on wysyłany z powrotem do aplikacji zamawiającej przez websocket. Dodatkowo wszystkie zewnętrzne wywołania używają precyzyjnych timeoutów i wzorców wyłączników dla lepszej niezawodności.
Należy wspomnieć, że wszystkie mikroserwisy dołączone do Kafka Stream są bezstanowe i mogą być skalowane horyzontalnie w odniesieniu do architektury Kafka. Kafka gwarantuje kolejność przetwarzania zdarzeń nawet przy wielu instancjach przetwarzających. Jest to osiągane poprzez umieszczenie wszystkich zdarzeń związanych z jednym zamówieniem w tej samej partycji tematycznej, a każda partycja może być przetwarzana tylko przez jeden wątek (jest to założenie projektowe).
Wszystkie te techniki sprawiają, że Ordering Stack jest systemem o wysokiej wydajności i dostępności, zdolnym do obsługi nawet wysokich szczytów.
Środowisko testowe i produkcyjne
Do hostingu naszych rozwiązań wykorzystujemy sprawdzonych w boju dostawców usług hostingowych i chmurowych takich jak 3S i Microsoft Azure. Obecnie wszystkie serwery znajdują się na terenie Unii Europejskiej.
Większość artefaktów systemowych jest skonteneryzowana za pomocą Dockera, używamy gitlab dla CI/CD i innych usług do monitorowania jak wszystkie nasze środowiska działają.
Zostańmy w kontakcie
Chcesz wiedzieć więcej?
Uwielbiamy rozmawiać o technologii!
info@orderingstack.com