Vai al contenuto

Quale architettura API scegliere?

In un mondo digitale sempre più invaso da microservizi, le API (Application Programming Interface) stanno prendendo sempre più piede. Attraverso le API possiamo comunciare e condividere dati a diversi sistemi softwaresenza soluzione di continuità. Ciascun sviluppatore, in fase di progettazione è chiamato a fare una scelta che risulterà di fondamentale importanza e condizionerà le fasi dello sviluppo. Scegliere infatti la giusta architettura API può decretare il successo o il fallimento di un progetto.

La scelta dell’architettura API perfetta dipende da vari fattori:

  1. Requisiti del progetto: le esigenze specifiche della propria applicazione
  2. Prestazioni: i requisiti previsti in termini di carico e tempo di risposta
  3. Scalabilità: la crescita futura e alle potenziali integrazioni
  4. Esperienza dello sviluppatore: la curva di apprendimento e gli strumenti disponibili
  5. Diversità dei clienti: i tipi di clienti che utilizzeranno la API

Questo articolo vuole dare una panoramica alle principali architetture API che dominano il mercato nel 2024, esplorandone i punti di forza, i casi d’uso e il modo in cui possono migliorare i nostri progetti.

1. SOAP (Simple Object Access Protocol)

SOAP esiste da un bel po’ e per una buona ragione. Questa architettura basata su protocollo è nota per i suoi rigorosi standard e le sue robuste funzionalità di sicurezza. E’ un protocollo che utilizza XML per la formattazione dei messaggi, supporta più protocolli (HTTP, SMTP, ecc.) ed è ottimo per applicazioni a livello aziendale, come ad esempio software per la gestione di servizi finanziari che richiedono elevata sicurezza, integrazioni di sistemi legacy e transazioni complesse con contratti dati rigorosi.

La struttura di un messaggio SOAP è un documento XML che contiene i seguenti elementi:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <m:GetStockPrice xmlns:m="http://www.example.org/stock">
      <m:StockName>GOOG</m:StockName>
    </m:GetStockPrice>
  </soap:Body>
</soap:Envelope>

2. RESTful (Representational State Transfer)

REST è diventata l’architettura di riferimento per le API web grazie alla sua semplicità e al suo allineamento con i protocolli HTTP. La sua caratteristica principale è che le interazioni sono stateless, “senza stato” in quanto non vengono utilizzati sessioni per salvare i dati generati e utilizzarli nel corso di sessioni successive. Ogni sessione viene pertanto eseguita senza alcuna memoria del pregresso, come se fosse sempre la sua prima volta. E’ perfettamente integrato al protocollo HTTP e utilizza i suoi metodi standard (ET, POST, PUT, DELETE). L’architettura RESTful è ideale per l’implementazione di API pubbliche, l’integrazione con applicazioni mobili e architetture di micreoservizi.

Di seguito una “chiamata” a un metodo API esposto al pubblico:

GET /api/users/123 HTTP/1.1
Host: example.com
Accept: application/json

3. GraphQL

GraphQL, sviluppato da Facebook, ha riscosso un’enorme popolarità grazie alla sua capacità di risolvere comuni problemi delle API REST, come l’eccesso o il mancato recupero dei dati. Le caratteristiche principali sono:

  • Query specificate dal client
  • Un unico endpoint per tutte le esigenze di dati
  • Schema fortemente tipizzato

Tali caratteristiche lo rendono ideale per lo sviluppo di applicazioni complesse con requisiti di dati diversificati o applicazioni mobili che necessitano di un caricamento dati efficiente. Esempio di una query:

{
  user(id: 1) {
    name
    age
    friends {
      name
    }
  }
}

mentre il risultato sarà una stringa json di questo tipo:

{
  "data": {
    "user": {
      "name": "Mario",
      "age": 28,
      "friends": [
        {
          "name": "Luigi"
        }
      ]
    }
  }
}

4. gRPC

gRPC, sviluppato da Google, è un frameword RPC open source i cui obiettivi sono elevate prestazioni ed efficienza, rendendolo la scelta ideale per le architetture di microservizi. Utilizza il protocollo HTTP/2 e Protobuf (Protocol Buffers) come formato di serializzazione per le comunicazioni, permettendo così di creare API ad alte prestazioni, affidabili e scalabili. Inoltre supporta lo streaming (unario, server, client e bidirezionale inviando un flusso continuo di messaggi su una singola connessione), l’autenticazione integrata ed è “agnostico” in termini di lingua, quindi funziona su più lingue e piattaforme (C++, Java, Python, Go, Ruby, e molte altre, rendendolo versatile per ambienti poliglotti). Tali caratteristiche lo rendono ideale per sviluppare microservizi che debbano comunicare tra di loro, oppure per applicazioni in real time che richiedono bassa latenza.

Immaginiamo di voler creare un semplice servizio gRPC che fornisce un saluto. Di seguito è riportato un esempio in cui il servizio riceve una richiesta con un nome e restituisce un messaggio di saluto.

1. Definizione del Servizio (file .proto)

Crea un file chiamato greet.proto:

syntax = "proto3";

service Greeter {
  // Definiamo un metodo RPC chiamato SayHello
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// Messaggio di richiesta
message HelloRequest {
  string name = 1;
}

// Messaggio di risposta
message HelloReply {
  string message = 1;
}

2. Generazione del Codice

Per generare il codice a partire dal file .proto, dovremo utilizzare il compilatore protoc. Supponiamo di usare Python:

python -m pip install grpcio grpcio-tools
python -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. greet.proto

Questo comando genera due file: greet_pb2.py e greet_pb2_grpc.py.

3. Implementazione del Server

Creiamo un file server.py per implementare il server gRPC:

from concurrent import futures
import grpc
import greet_pb2
import greet_pb2_grpc

class Greeter(greet_pb2_grpc.GreeterServicer):
    def SayHello(self, request, context):
        return greet_pb2.HelloReply(message=f'Ciao, {request.name}!')

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    greet_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    print("Server avviato su porta 50051")
    server.wait_for_termination()

if __name__ == '__main__':
    serve()

4. Implementazione del Client

Creiamo un file client.py per implementare il client gRPC:

import grpc
import greet_pb2
import greet_pb2_grpc

def run():
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = greet_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(greet_pb2.HelloRequest(name='Mario'))
    print("Risposta del server:", response.message)

if __name__ == '__main__':
    run()

5. Esecuzione

  1. Avviamo il server eseguendo python server.py
  2. Poi eseguiamo il client con python client.py

Dovremmo vedere l’output:

Risposta del server: Ciao, Mario!

5. WebSocket

Quando abbiamo la necessità di canali di comunicazione full-duplex persistenti, WebSocket è l’architettura che fa per noi. Websocket infatti è il re dello streaming, supporta la comunicazione bidirezionale che lo rendoono ideale per lo sviluppo di tutte quelle applicazioni che richiedono trasmissione di dati live, dalla chat, agli strumenti collaborativi agli eventi sportivi in diretta. Il concetto si basa sulla creazione di un canale di comunicazione chiamato appunto Websocket. Ecco un esempio:

const socket = new WebSocket('ws://example.com/socket');

socket.onopen = function(event) {
  socket.send('Hello Server!');
};

socket.onmessage = function(event) {
  console.log('Message from server:', event.data);
};

I WebSocket sono ideali quando è necessario mantenere una connessione aperta e reattiva tra client e server, offrendo un’alternativa più efficiente rispetto ai tradizionali metodi basati su HTTP.

6. Webhook

I webhook capovolgono il tradizionale modello request-response: consentono infatti ai server di inviare dati ai client quando si verificano eventi specifici (event-response). In tal modo si riduce il polling e il carico sul server. Quindi si tratta di un’architettura basata sugli eventi. L’aggiornamento avviene in tempo reale. I Webhook si possono usare per sistemi di elaborazione dei pagamenti e notifiche, Pipeline CI/CD e per aggiornamenti dei dispositivi IoT.

Tecnicamente la chiamata è molto simile, se non uguale ad una chiamata API, ma gli webhook, quindi, sono molto più efficienti rispetto alle API quando si tratta di comunicare la creazione o l’aggiornamento di dati che non vengono modificati spesso.

OST /webhook HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "event": "payment_received",
  "data": {
    "amount": 100,
    "currency": "USD",
    "customer_id": "cus_123"
  }
}
In conclusione, non esiste una soluzione adatta a tutti. La migliore architettura per il nostro progetto dipende dai requisiti specifici, dall’esperienza del team e dagli obiettivi a lungo termine. Non dobbiamo aver paura di mescolare e abbinare queste architetture per creare una soluzione ibrida che si adatti perfettamente alle nostre esigenze.