Modelowanie HTTP API na kształt Remote Procedure Call (RPC)
Proponuję, aby dla skomplikowanych procesów budować HTTP API w sposób przypominający zdalne wywoływanie procedur.
CRUD vs Business Operations
W pierwszej kolejności, zanim zaczniesz czytać dalej, koniecznie przeczytaj ten wpis o projektowaniu REST API Design – Resource Modeling od Thoughtworks.
RPC & SOA vs REST & CRUD over HTTP API
To był wcześniejszy tytuł tego tekstu, który nie za bardzo się kliknął, ale zostawiłem go, ponieważ jeśli już ktoś dotarł tutaj, to ten tytuł w maksymalnie skondensowanej formie wyjaśnia na czym polega pomysł rozwiązania i równie zwięźle podsumowuje wyżej linkowany artykuł o projektowaniu REST API.
Domena
Jako przykład operacji biznesowej posłuży mi domena nauki wraz z jej artykułami naukowymi, które podlegają procesowi recenzji przed opublikowaniem. W tej domenie miałem okazję pracować przez ostatnie dwa lata.
Praca naukowa, która pomyślnie przeszła proces recenzji jest publikowana w czasopiśmie naukowym. Manuskryptem będziemy nazywać pracę naukową, która jest w trakcie recenzji. W celu przeprowadzenia recenzji, recenzenci są zapraszani dla konkretnego manuskryptu. Oni decydują o jego akceptacji lub odrzuceniu. Manuskrypt zaakceptowany jest następnie wysyłany do czasopisma naukowego, aby mógł zostać opublikowany jako artykuł.
Implementacja
Ogólny wzorzec operacji biznesowych na poziomie API przedstawia się następująco: na poziomie zasobów (resources) dodaje się nowy zasób o nazwie „actions„. Następnie, w obrębie „actions„, dodawana jest konkretna operacja biznesowa, czyli czasownik (action) w formie rozkazującej.
POST /{resource}/{id}/actions/{action}
Przykładem synchronicznej operacji biznesowej może być zaproszenie recenzenta oraz akceptacja lub odrzucenie manuskryptu. Każda z tych operacji biznesowych może być realizowana przez inny proces, który zależy od konfiguracji specyficznej dla poszczególnych magazynów naukowych.
POST /manuscripts/256/actions/invite
POST /manuscripts/256/actions/accept
POST /manuscripts/256/actions/reject
Sync
W odpowiedzi zwracany jest status 204 (No Content).
204 No Content
Async
W odpowiedzi zwracany jest status 202 (Accepted), a w nagłówku „Location” podawany jest link, za pomocą którego można sprawdzić status realizowanego procesu.
202 Accepted
Location: /{resources}/{id}/actions/{action}/{actionId}
Powodem modelowania asynchronicznej operacji biznesowej może być proces, w którym wymagane są więcej niż jeden krok (na przykład dodatkowa zgoda redaktora magazynu naukowego) lub gdy operacja jest czasochłonna i nie oczekuje się, że klient będzie czekać na jej zakończenie.
Przykładowo, publikację artykułu naukowego w magazynie naukowym można zamodelować w ten sposób.
POST /journals/16384/actions/submit
Status: 202 Accepted
Location: /journals/16384/actions/submit/4096
Następnie klient, korzystając z otrzymanego linku, wykonuje zapytanie.
GET /journals/16384/actions/submit/4096
W przypadku, gdy akcja nadal jest w trakcie wykonywania, wówczas zwracany jest status 102 (Processing) wraz z nagłówkiem „Retry-After„, który informuje, kiedy klient powinien wykonać ponowne sprawdzenie statusu.
102 Processing
Retry-After: 30
W przypadku, gdy akcja została wykonana w całości, wówczas zwracany jest status 204 (No Content).
204 No content
Sync lub Async
W szczególnych przypadkach możemy dać klientowi możliwość wyboru, czy dana operacja ma być wykonywana synchronicznie, czy asynchronicznie.
Operacja zostanie wykonana synchronicznie, jeśli klient podczas wywoływania API ustawi nagłówek „Expect” z wartością statusu 204 (No Content).
POST /journals/16384/actions/submit
Expected: 204-no-content
Operacja zostanie wykonana asynchronicznie, jeśli klient podczas wywoływania API ustawi nagłówek ‚Expect’ z wartością statusu 202 (Accepted).
POST /journals/16384/actions/submit
Expected: 202-accepted
W przypadku, gdy wybrany przez klienta oczekiwany sposób wykonania nie jest dostępny, wówczas akcja zwraca status 417 (Expectation Failed).
417 Expectation Failed
Acknowledgement
W ostatnim roku pracowałem nad projektem, w którym modelowaliśmy HTTP API w stylu Remote Procedure Call (RPC). Architektem i pomysłodawcą tego podejścia był Konrad Kwiatkowski, który kompletnie zmienił moje postrzeganie świata, przekonując mnie, że nie musimy dążyć do osiągnięcia czwartej, a nawet półtorej wersji dojrzałości modelu Richardsona dla REST API. Za tę perspektywę jestem mu niezmiernie wdzięczny.