# Standard della piattaforma

## **Struttura Eventi**

### **Producers**

Nella piattaforma si fa ampio uso del **pattern** [**Event Sourcing**](https://martinfowler.com/eaaDev/EventSourcing.html), perciò è fondamentale che il **formato degli eventi** sia **stabile**, **versionato** e **documentato**.

Tutti i producer DEVONO includere **obbligatoriamente** i seguenti campi in ogni evento:

| Campo              | Valore                                                    | Formato                 | Annotazioni                                                                                 |
| ------------------ | --------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------- |
| `id`               | identificativo unico dell'entità oggetto dell'evento      | UUID                    | Es: in un pagamento è l'ID del pagamento, in una pratica è l'application\_id etc...         |
| `event_id`         | identificativo unico dell'evento                          | UUID                    | Non deve esserci per nessun motivo due volte lo stesso ID                                   |
| `event_version`    | versione dell'evento                                      | Integer or Float        | E' un intero (Es: 1, 2, etc..) cambia ad ogni cambiamento NON retrocompatibile.             |
| `event_created_at` | data dell'evento                                          | ISO8601                 | Ricordarsi di usare il formato della timezone Europe/Rome (Es: `2022-06-22T15:11:20+02:00`) |
| `app_id`           | Nome e versione dell'applicativo che ha generato l'evento | `$application:$version` | Utile per motivi di debug (Es: payment-dispatcher:1.0.15).                                  |

> **Nota importante:** Se si genera un nuovo evento **a partire da uno esistente**, **NON** vanno ricopiati i campi `event_id`, `event_created_at`, `event_version`, `app_id`. Si tratta di una **nuova entità**, quindi deve avere **nuovi valori**.

### **Consumers**

Chi consuma eventi deve:

* **Gestire esplicitamente** le versioni supportate.
* **Scartare** eventi con versioni non supportate.
* **Non** generare errori per versioni non gestite:
  * È corretto **loggare in DEBUG** la ricezione di una versione non supportata.
  * In condizioni normali, un consumer **emette un log a livello INFO solo se**:
    * ha processato con successo l’evento.
    * ha eseguito un'azione concreta.

> Questo approccio evita rumore nei log e rende evidenti solo le operazioni significative.

## Sistema di Retry

### **Obiettivo**

La piattaforma è dotata di un componente, detto RetryOrchestrator, responsabile della gestione automatica dei **retry** per eventi temporaneamente non processabili. In caso di errore temporaneo, consente di riprogrammare la consegna dell’evento al consumer in base a una **politica configurabile**, evitando la perdita di messaggi e riducendo l'intervento manuale.

### **Funzionamento**

1. Quando un **servizio X** fallisce nel processare un messaggio, questo viene inviato nel **Failed Message Topic (FMT)**.
2. Il **RetryOrchestrator** legge i messaggi dal FMT e, sulla base della politica di retry, li **ripropone al servizio X**.
3. Se il messaggio esaurisce i tentativi previsti, viene inviato in una **Dead Letter Queue (DLQ)**.

### **Politica di Retry**

La politica di retry viene definita tramite due variabili d’ambiente principali:

| Variabile                  | Descrizione                                                                                                                                        |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `KAFKA_DISPATCHER_POLICY`  | Specifica la sequenza dei retry come array di coppie `NxTm`, dove `N` è il numero di tentativi e `Tm` è il tempo di attesa (es. `2x1m,1x2m,3x3m`). |
| `KAFKA_RETRY_QUEUE_PREFIX` | Prefisso dei topic di attesa su Kafka (es. `retryQueue_`).                                                                                         |

#### **Esempio**

Con la configurazione:

* `KAFKA_DISPATCHER_POLICY=2x1m,1x2m,3x3m`&#x20;
* `KAFKA_RETRY_QUEUE_PREFIX=retryQueue_`

Il servizio si aspetta i seguenti topic di retry:

* `retryQueue_1m`
* `retryQueue_2m`
* `retryQueue_3m`

Un messaggio attraverserà quindi una sequenza di 6 tentativi: 2 tentativi a distanza di 1 minuto, 1 tentativo dopo 2 minuti, 3 tentativi dopo 3 minuti ciascuno.

## Standard Date

### Formato delle date

Tutte le date all’interno della piattaforma devono essere rappresentate nel **formato ISO 8601**, con la seguente struttura:

```
YYYY-MM-DDTHH:MM:SS+HH:MM
```

Esempio:

```
2025-03-12T00:00:00+01:00
```

> Questo formato garantisce coerenza, leggibilità e interoperabilità tra i diversi sistemi e microservizi, soprattutto in contesti distribuiti.

### Fuso orario

Tutte le date devono essere espresse nel **fuso orario `Europe/Rome`**, che gestisce automaticamente l’**ora solare** (`+01:00`) e l’**ora legale** (`+02:00`).

> Non utilizzare `UTC` o altri fusi orari, salvo esplicita eccezione documentata.

### Linee guida

* Le date devono **includere l’offset del fuso orario** (`+01:00` o `+02:00`) come parte della stringa.
* Le librerie utilizzate per serializzazione/deserializzazione devono essere configurate per usare **`Europe/Rome`** come timezone di default.
* Il formato deve essere rispettato sia in **input** che in **output** per tutte le API e gli eventi.

#### Applicazioni

| Contesto          | Applicazione dello standard                      |
| ----------------- | ------------------------------------------------ |
| API REST          | Tutte le date nei payload (richieste e risposte) |
| Messaggi su Kafka | Timestamp negli eventi pubblicati                |

#### Validazioni e test

* Ogni data deve essere validata secondo il pattern ISO8601 con timezone `Europe/Rome`.
* I test automatici devono includere:
  * Verifica del formato in fase di creazione e lettura degli eventi.
  * Verifica dell’offset corretto (`+01:00` o `+02:00`) in base alla data.
  * Gestione corretta dell’ora legale.

## API Standards

### **Principi di riferimento**

Le API della piattaforma seguono i principi di:

* [Zalando RESTful API Guidelines](https://opensource.zalando.com/restful-api-guidelines/)
* [Linee guida di interoperabilità della PA (AgID)](https://docs.italia.it/AgID/documenti-in-corso/lg-interoperabilita-docs/)

### **Comportamento dei servizi**

* I servizi DEVONO **esporre solo i verbi HTTP previsti** e restituire **errore per metodi non supportati**.
* I metodi seguenti devono essere **idempotenti**, come da [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231):
  * `DELETE`, `GET`, `HEAD`, `OPTIONS`, `PUT`, `TRACE`
* I percorsi (path) delle risorse devono:
  * Essere **al plurale** per rappresentare collezioni
  * Usare **kebab-case** (es. `user-preferences`)

### **Esempi di endpoint**

```http
POST    /tenants
GET     /tenants/{tenant_id}
PUT     /tenants/{tenant_id}
PATCH   /tenants/{tenant_id}
DELETE  /tenants/{tenant_id}
```

> Le URL nelle risposte devono essere sempre espresse in forma **assoluta**.

### GET e Navigazione

Per le risorse collezione, le risposte devono includere una struttura di **meta-informazioni** e **link di navigazione**:

```json
{
  "meta": {
    "page": {
      "offset": 5000,
      "limit": 20,
      "sort": "creationTime"
    },
    "total": 46561
  },
  "links": {
    "self": "https://api.example.it/items?offset=5000&limit=20&sort=creationTime",
    "prev": "https://api.example.it/items?offset=4980&limit=20&sort=creationTime",
    "next": "https://api.example.it/items?offset=5020&limit=20&sort=creationTime"
  },
  "data": [
    { "id": "..." },
    { "id": "..." },
    { "id": "..." }
  ]
}
```

### **Paginazione**

I parametri di paginazione da usare nei `GET`, ove applicabile, sono:

| Parametro | Descrizione                                     |
| --------- | ----------------------------------------------- |
| `offset`  | Offset dell’elemento iniziale                   |
| `limit`   | Numero massimo di elementi da restituire        |
| `sort`    | Ordinamento (campo o criterio)                  |
| `cursor`  | Puntatore per navigazione cursor-based          |
| `embed`   | Specifica risorse correlate da includere        |
| `q`       | Query full-text o filtro libero                 |
| `fields`  | Specifica dei campi da includere nella risposta |

Per endpoint con elevata numerosità di dati, si può usare la **cursor-based pagination**, ad esempio:

```http
GET /payments?created_since=2024-12-01T00:00:00+01:00
```

### Gestione degli errori

Tutti gli errori devono seguire lo standard [RFC 7807 - Problem Details for HTTP APIs](https://datatracker.ietf.org/doc/html/rfc7807):

```json
{
  "type": "/errors/incorrect-user-pass",
  "title": "Incorrect username or password.",
  "status": 401,
  "detail": "Authentication failed due to incorrect username or password.",
  "instance": "/login/log/abc123"
}
```

> Ogni errore può includere:
>
> * `type`: URL che identifica il tipo di errore
> * `title`: breve descrizione dell’errore
> * `status`: HTTP status code (opzionale)
> * `detail`: descrizione dettagliata (opzionale)
> * `instance`: identificatore specifico dell’occorrenza (opzionale)
