RPC: kiedy API modeluje operacje zamiast zasobów
- data
- kategoria
- Backend
- także w
- Software Architecture · System Design
- czytanie
- 1 min / 290 słów
REST zaczyna od zasobu:
POST /orders
PATCH /orders/42
RPC zaczyna od operacji:
POST /createOrder
POST /approveOrder
POST /cancelOrder
RPC oznacza Remote Procedure Call.
Mental model jest prosty:
local function call
|
v
network
|
v
remote execution
Procedura zamiast zasobu
Lokalnie moglibyśmy napisać:
approveInvoice(42);
RPC próbuje zachować podobną semantykę przez granicę sieci:
POST /rpc/approveInvoice
{
"invoiceId": 42
}
Backend mapuje request na operację domenową.
To często lepiej odpowiada systemom, w których najważniejsze są komendy, a nie CRUD.
RPC nie eliminuje HTTP
RPC jest modelem aplikacyjnym.
Transport nadal może być HTTP:
RPC method
|
v
HTTP request
|
v
TCP/QUIC
Możliwe jest nawet używanie jednego endpointu:
POST /rpc
{
"method": "approveInvoice",
"params": {
"invoiceId": 42
}
}
Tak działa między innymi styl JSON-RPC.
Zaleta: semantyka domeny jest jawna
Porównaj:
PATCH /payments/42
{
"status": "retried"
}
z:
POST /payments/42/retry
albo RPC:
retryPayment(42)
Druga forma mówi więcej o intencji.
Status nie zawsze jest danymi wejściowymi. Często jest skutkiem wykonania operacji.
RPC potrafi lepiej modelować takie przypadki.
Cena: tracimy część semantyki HTTP
Jeżeli wszystko jest:
POST /rpc
HTTP wie bardzo mało o znaczeniu operacji.
Nie może łatwo wywnioskować:
- czy operacja jest safe,
- czy jest idempotentna,
- czy odpowiedź nadaje się do cache,
- czy retry jest bezpieczny.
Semantyka przenosi się z warstwy HTTP do kontraktu aplikacji.
Dlatego RPC wymaga bardzo dobrego kontraktu.
Typed RPC
Nowoczesne systemy RPC często idą dalej:
schema
|
v
code generation
|
v
typed client
|
v
typed server
gRPC używa do tego Protocol Buffers.
Inne narzędzia generują klienta bezpośrednio z definicji endpointów lub typów.
To zmniejsza dystans między:
client.approveInvoice({ id: 42 })
a zwykłym wywołaniem funkcji.
Ale sieć nadal pozostaje siecią:
- może wystąpić timeout,
- request może dotrzeć mimo utraty odpowiedzi,
- retry może wykonać operację ponownie,
- klient i serwer mogą mieć różne wersje kontraktu.
RPC upraszcza interfejs, ale nie usuwa semantyki systemu rozproszonego.
Kolejne ograniczenie
REST i RPC mają wspólną cechę:
backend definiuje kształt endpointu i odpowiedzi.
Jeżeli różne ekrany potrzebują różnych podzbiorów i kombinacji danych, liczba endpointów lub wariantów odpowiedzi zaczyna rosnąć.
Można wtedy odwrócić model.
Zamiast pytać konkretny endpoint o z góry ustalony payload, klient może powiedzieć dokładnie, jakich danych potrzebuje.
To jest punkt wejścia do GraphQL.