Rest API Design - Funziona con ID o stringhe letterali?

6

Quando si progetta un servizio Web RESTful, l'API deve essere progettata per funzionare ID per le stringhe per i valori passati avanti e indietro tra il server?

Ecco un esempio: diciamo che ho una risorsa Employee, che ha uno status e attributi di genere. Nel database Stato e Sesso e tabelle separate e quindi separato oggetto Dominio, ciascuno con il proprio identificativo.

Diciamo che la richiesta / dipendente del cliente / 1. Il server potrebbe restituire qualcosa del genere ....

Caso 1:

{
    "id": 1,
    "firstName": "Jane",
    "lastName": "Doe",
    "active": true,
    "gender": {
        "id": 1,
        "gender": "FEMALE"
    },
    "status": {
        "id": 3,
        "status": "FULL_TIME"
    }
}

Caso 2:

{
    "id": 1,
    "firstName": "Jane",
    "lastName": "Doe",
    "active": true,
    "gender": "FEMALE",
    "status": "FULL_TIME"
}

Caso 3:

{
    "id": 1,
    "firstName": "Jane",
    "lastName": "Doe",
    "active": true,
    "genderId": 1,
    "statusId": 3
}

Il caso 3 sembra aver senso, dal momento che il cliente non ha idea di cosa sia genderId 1 a meno che non si rigiri e faccia un'altra chiamata al server per ottenere quei dati.

Tuttavia ora diciamo che il client sta aggiornando l'utente tramite:

PUT /employee/1

La richiesta Payload dovrebbe usare gli id o una stringa? In entrambi i casi, il back-end deve cercarli per assicurarsi che siano validi, ma è meglio lavorare con gli ID su Strings.

    
posta jkratz55 23.02.2017 - 15:39
fonte

5 risposte

3

Let's say I have an Employee resource, which has a status and gender attributes. In the database Status and Gender and separate tables and thus separate Domain object, each with its own identifier.

Le rappresentazioni API non devono essere strettamente associate ai dettagli dell'implementazione. Direi che ricavare le rappresentazioni API dai dettagli dell'implementazione è esattamente al contrario.

Pensa a Adapter Pattern dal libro Gang of Four . I messaggi del web sono quelli di un negozio di documenti. Il tuo obiettivo nella creazione di un'API è produrre i documenti che i tuoi consumatori desiderano, isolandoli dai dettagli nitidi di produzione di tali documenti.

La motivazione per farlo è che puoi cambiare i dettagli di implementazione ogni volta che vuoi, con la certezza che - finché non cambierai le rappresentazioni che torni, i tuoi clienti non si romperanno.

Inoltre, tieni presente che una singola risorsa logica potrebbe avere molte rappresentazioni, solo alcune delle quali supportano la modifica.

let's say the client is updating the user

In quanto consumatore, con quale rappresentazione vuoi lavorare? La mia ipotesi è che il più vicino è

{
    "firstName": "Jane",
    "lastName": "Doe",
    "active": true,
    "gender": "FEMALE",
    "status": "FULL_TIME"
}

Se Metto questa rappresentazione in una posizione che specifichi, dovresti essere in grado di capire il resto.

Se stavi creando rappresentazioni per le macchine da utilizzare, probabilmente dovresti volere meno ambiguità nell'ortografia

{
    "https://schema.org/givenName": "Jane",
    "https://schema.org/familyName": "Doe",
    "active": true,
    "https://schema.org/gender": "https://schema.org/Female",
    "https://schema.org/employmentType": "FULL_TIME"
}

Stessa risorsa logica, due diverse rappresentazioni. Cavalli per i corsi.

    
risposta data 25.02.2017 - 07:23
fonte
1

Sia il caso 1 che il caso 2 hanno un bell'aspetto. La scelta può essere prevista dal modo in cui organizzi il tuo modello di dominio.

Hai rispecchiato le tabelle Employee, Gender e Status nel dominio (usando ORM suppongo). Ciascuna di queste classi in questo particolare modello è una entità che ha proprio identificativo . L'ulteriore esposizione dell'intero modello tramite API REST sembra logica e adatta al caso 1.

In alternativa, puoi attenersi ai principi DDD che prestano molta attenzione alle differenze tra entità e oggetti valore . Da questo punto di vista, Employee è un'entità (con id) e Gender e Status potrebbero essere buoni candidati per diventare value objects (incorporato nell'entità Employee, senza identificatori). Questo si adatta al caso 2.

Pienamente d'accordo con te sul fatto che Case 3 è un no.

    
risposta data 23.02.2017 - 23:27
fonte
1

Il caso 2 è l'unica opzione reale. Hai già indicato i problemi relativi al caso 3. Il caso 1 fornisce informazioni a cui il client dell'API non interessa (gli ID interni per gli stati, ad esempio) e richiede al cliente di sapere su chi costruire una richiesta PUT . Sì, la richiesta PUT è un po 'più concisa se può utilizzare gli ID invece delle stringhe complete, ma specificare "FULL_TIME" o "PART_TIME" è ciò che sa il client, non che capita di avere alcuni ID arbitrari nel database .

Naturalmente, puoi documentare gli ID nella documentazione della tua API, ma è altrettanto facile documentare i valori validi che le stringhe possono essere e, probabilmente, più chiari.

    
risposta data 23.02.2017 - 23:40
fonte
0

I dati enumerati come quelli che hai qui sono altamente memorizzabili nella cache. Usa link invece di oggetti. Utilizza le intestazioni di cache per consentire ai client di memorizzare nella cache i generi e gli stati localmente, diciamo per 24 ore. Quindi solo la prima chiamata del giorno lascia la macchina client. Probabilmente puoi anche configurare la memorizzazione nella cache per consentire ai server intermedi di conservare le informazioni, quindi alcune richieste dei client non arrivano nemmeno sul tuo server.

GET /employees/1
{
    "id": 1,
    "firstName": "Jane",
    "lastName": "Doe",
    "active": true,
    "gender": "/genders/1",
    "status": "/statuses/3"
}

// clients populate their dropdown with
GET /genders
[
    {"id":1, "gender":"FEMALE"},
    {"id":2, "gender":"MALE"},
    ...
]

// clients look up an employee's gender with
GET /genders/1
{
    "id": 1,
    "gender": FEMALE
}

Uno svantaggio è che /genders/1 non è leggibile dall'uomo. Puoi invece utilizzare /genders/female , ma non puoi mai cambiare il nome di un sesso senza rompere i client. Questa è la chiave sintetica rispetto al compromesso tra le chiavi naturali: flessibilità rispetto alla leggibilità umana.

Potresti anche prendere in considerazione la possibilità di inserire tutti i tuoi elenchi di valori in un unico endpoint comune, come

/lists/genders/1
/lists/statuses/3

Questo chiarirà ai clienti che sono tutte coppie di valori-chiave che appartengono a diversi raggruppamenti.

    
risposta data 24.02.2017 - 16:48
fonte
0

Mi piacerebbe fare qualcosa tra 1 e 2, per i motivi che David ha menzionato:

Non vuoi esporre l'ID delle cose se non necessario.

Tuttavia, l'esposizione dell'ID potrebbe diventare necessaria a un certo punto nel tempo. Se ciò accade, la retrocompatibilità è una preoccupazione. Quindi, vorrei fare questo:

{
    "id": 1,
    "firstName": "Jane",
    "lastName": "Doe",
    "active": true,
    "gender": {
        "name": "FEMALE"
    },
    "status": {
        "name": "FULL_TIME"
    }
}

Ha le stesse proprietà dell'opzione 2; ma ha il vantaggio che l'aggiunta dell'ID in seguito non introduce un'interruzione BC:

{
    "id": 1,
    "firstName": "Jane",
    "lastName": "Doe",
    "active": true,
    "gender": {
        "id": 1,
        "name": "FEMALE"
    },
    "status": {
        "id": 3,
        "name": "FULL_TIME"
    }
}

Come sottolinea Eric nei commenti, questo usa ancora il nome dell'entità come identificatore di uniqe. Se l'ID viene introdotto in un secondo momento, il nome deve rimanere invariato, perché i client più vecchi potrebbero (o meglio lo saranno ) avere codificato per questo.

    
risposta data 24.02.2017 - 00:51
fonte

Leggi altre domande sui tag