Architektura opiera się na wzorcu Saga Orchestration, oddzielonym przez backbone z Event-Driven. Na wejściu, API Gateway (Kong lub Envoy) weryfikuje tokeny JWT i kieruje żądania do Policy Enforcement Point (PEP), które zapytuje Policy Decision Point (PDP) korzystając z Open Policy Agent (OPA) w celu przeprowadzenia bieżących kontroli AML i KYC względem list sankcyjnych.
Rdzeń to Cross-Ledger Transaction Coordinator, zaimplementowany jako maszyna stanów przy użyciu Temporal lub własnego silnika Saga na Apache Kafka. Ten koordynator zarządza rozproszoną transakcją w dwóch odrębnych domenach: Fiat Ledger Adapter (integrujący z SWIFT, ACH lub SEPA za pomocą komunikacji ISO 20022) oraz Blockchain Adapter (wspierający łańcuchy EVM poprzez Alchemy lub Infura, i Stellar przez Horizon API).
Dla atomowości bez 2PC (które jest niedostępne na publicznych blockchainach) stosujemy wzorzec Saga z transakcjami kompensacyjnymi. Koordynator najpierw wykonuje lokalną transakcję "debit fiat", a następnie lokalną transakcję "mint/transfer stablecoin". Jeśli ta ostatnia się nie powiedzie, pierwsza jest rekompensowana przez transakcję "credit fiat". Event sourcing zapewnia, że wszystkie zmiany stanu są utrwalane w PostgreSQL i publikowane do Kafka dla audytowalności.
Zarządzanie płynnością wykorzystuje Geographically Distributed Cache (Redis Cluster) z WAL jako wsparcie dla Cassandra dla spójności międzyregionowej. Połączenia gRPC między mikroserwisami zapewniają niską latencję, podczas gdy Prometheus i Grafana zapewniają obserwowalność. Cała wtyczka działa na Kubernetes z Istio dla możliwości sieci serwisów, zapewniając mTLS między komponentami.
W CrossBridge Payments stanęliśmy przed krytycznym wymogiem umożliwienia natychmiastowej przekazania pieniędzy od klienta z USA korzystającego z ACH do niemieckiego odbiorcy otrzymującego kredyty SEPA, trasowanych przez most USDC na Ethereum i Stellar, aby zmniejszyć opóźnienia w bankowości korespondencyjnej. Głównym wyzwaniem była atomowość: jeśli transakcja blockchainowa nie powiedzie się po sukcesie debetu ACH, klient straciłby środki, podczas gdy finalizacja blockchainowa trwa 12 sekund na Ethereum, podczas gdy rozliczenie ACH zajmuje T+1, ale debety są natychmiastowe.
Oceniliśmy trzy podejścia architektoniczne. Pierwsza opcja obejmowała Centralized Oracle, który miałby pieczę nad zarówno fiat, jak i kryptowalutą, działając jako zaufany pośrednik. Chociaż to upraszczało koordynację i zmniejszało latencję do milisekund, wprowadzało nieakceptowalne ryzyko stron i nie spełniało wymagań regulacyjnych dotyczących zdecentralizowanej pieczy w niektórych jurysdykcjach.
Druga opcja proponowała Hash Time-Locked Contracts (HTLC) do bezstronnych wymian atomowych między bankiem fiat a blockchainem. Jednak to okazało się niewykonalne, ponieważ tradycyjne metody bankowe brakuje kryptograficznych prymitywów do weryfikacji hashy na łańcuchu, a mechanizmy czasowe wprowadzały złą jakość doświadczenia użytkownika, wymagając aktywnego udziału klienta.
Ostatecznie wybraliśmy Saga Orchestration z Event Sourcing wykorzystując Apache Kafka i Temporal. To podejście traktowało debet fiat i mintowanie kryptowaluty jako oddzielne lokalne transakcje w ramach Saga. Orkiestrator najpierw zablokował środki na głównym koncie escrow przez adapter ACH, a następnie zainicjował transfer USDC na Stellar (wybór z finalizacją w 5 sekund). Jeśli krok kryptograficzny się nie powiedzie, orkiestrator uruchamia transakcję kompensacyjną, aby cofnąć blokadę ACH.
Rezultat to 99,95% wskaźnik sukcesu z średnim czasem potwierdzenia UI wynoszącym 800 ms, pełne audyty regulacyjne przechowywane w PostgreSQL oraz zero strat środków klientów z powodu awarii atomowości w trakcie sześciomiesięcznego pilotażu.
Jak godzić synchroniczną naturę oczekiwań klientów REST API z asynchroniczną, probabilistyczną finalnością publicznych sieci blockchain, nie trzymając połączeń HTTP otwartych przez minuty?
Wielu kandydatów sugeruje długie pole, lub blokujące żądania HTTP, aż do potwierdzenia blockchain, co wyczerpuje wątki serwera i powoduje przekroczenia limitów bramek. Prawidłowe podejście wiąże się z wzorcem CQRS połączonym z Event Sourcing. Początkowe zapytanie o rozliczenie zwraca natychmiast z statusem 202 Accepted oraz unikalnym identyfikatorem korelacji transakcji. Klient subskrybuje WebSocket lub punkt końcowy Server-Sent Events (SSE), lub pytania do punktu końcowego statusu wspieranego przez Redis. Backend przetwarza potwierdzenie blockchain asynchronicznie za pośrednictwem konsumentów Kafka. Gdy Saga osiąga stan terminalny (ukończony lub skompensowany), status jest przesyłany do klienta.
Jaka strategia zapewnia dokładne jednorazowe wykonanie debetów fiat, gdy API bankowe (JPMorgan Access lub Stripe Treasury) zwraca timeout, pozostawiając niepewność co do tego, czy środki faktycznie zostały przeniesione?
Kandydaci często błędnie zakładają, że ponowne próby są bezpieczne lub że klucze idempotencje same wystarczą. Solidne rozwiązanie implementuje Idempotency Ledger przy użyciu PostgreSQL z maszyną stanów PENDING. Przed wywołaniem zewnętrznego API, usługa zapisuje rekord zamiaru z deterministycznym kluczem (SHA-256 identyfikator transakcji + kubełek znacznika czasowego). Jeśli API nie odpowiada, w tle Saga worker pyta endpoint zapytania o idempotencję banku (lub używa Webhook reconciliacji). Tylko po wyraźnym potwierdzeniu lub zaprzeczeniu stan przechodzi do SUCCESS lub FAILED.
Jak zapobiec fragmentacji płynności i podwójnemu wydawaniu w współdzielonym zbiorze płynności, gdy boty arbitrażowe o wysokiej częstotliwości jednocześnie uzyskują dostęp do tych samych rezerw USDC przez REST API i nadchodzące wydarzenia depozytowe blockchain?
Wymaga to Optimistic Locking na poziomie bazy danych i Distributed Locking dla krytycznych sekcji. Usługa płynności utrzymuje wersjonowane wiersze w PostgreSQL; każda aktualizacja zwiększa wersję. Gdy próba wypłaty jest podejmowana, system sprawdza wersję. Jeśli równoległe wydarzenie blockchain zmodyfikowało wiersz (mismatch wersji), transakcja jest ponawiana. Dla gorącej ścieżki, nabywana jest Redis Redlock przed sprawdzeniem sald, zapewniając sekwencyjny dostęp. Dodatkowo, monitoring zawartości zbioru płynności przez Circuit Breaker (Resilience4j) monitoruje wskaźnik obciążenia.