Resto anemico e DDD

5

Assumi il seguente modello di dominio:

Un [Ordine] consiste di un numero di proprietà, nonché un elenco di [OrderLine] s.
Per il nostro scopo, un ordine è abbastanza grossolano con una logica aziendale molto piccola.

Considera la seguente user story: un utente recupera un ordine. Quindi cancella una riga d'ordine e ne aggiunge altre 2. Alla fine, salva l'ordine.

Come sarebbe implementato in REST? Vedo alcune opzioni, ma ognuna con alcuni svantaggi:

  1. Approccio a grana grossa:
    L'azione di salvataggio comporterà una richiesta PUT (PUT / ordini / 3) contenente l'intero ordine, comprese le righe dell'ordine. Lo svantaggio è che è difficile capire cosa è cambiato (ovvero quali linee d'ordine dovrebbero essere aggiunte / aggiornate / cancellate?).
    Riesco a vedere questo trasformarsi in un disastro reale rapidamente quando più tardi si scopre che c'è una specifica logica di business quando si aggiunge una riga d'ordine. Sostanzialmente si riduce a sostituire un ordine con un altro e incrociamo le dita che tutto è valido, a meno che non decidiamo di implementare nuovamente le regole di business lato client.

  2. Approccio a grana fine:
    L'azione Elimina riga d'ordine darà come risultato una richiesta DELETE (DELETE / orders / 3 / lines / 5). L'azione Aggiungi riga ordine darà come risultato una richiesta POST (POST / ordini / 3 / linee). Ciò implica che l'app deve ora tenere traccia di molte modifiche ed eseguire le modifiche nell'ordine corretto quando l'utente decide di salvare.
    Un altro svantaggio è che quando viene creato un nuovo ordine, prima deve essere salvato prima di poter aggiungere righe di ordine.

Dal momento che il resto tratta i documenti non sono sicuro di come questi si tradurranno in operazioni eseguite su un modello di dominio, dato che i documenti sono anemici e i modelli di dominio non lo sono.

    
posta Bart 08.02.2017 - 03:06
fonte

4 risposte

3

Since rest deals with documents I'm not sure how those would translate into operations executed on a domain model, given that documents are anemic and domain models are not.

La cosa principale da tenere a mente è il point di una web api è di adattare il tuo dominio per il web; vale a dire, per dare l'illusione che le tue risorse siano in realtà solo voci in un archivio di documenti.

In un certo senso, un consumatore dovrebbe essere in grado di guidare la tua API usando un editor di documenti basato sul web.

Approccio a grana grossa

The save action will result in a PUT request (PUT /orders/3) containing the entire order including order lines. The drawback is that it's hard to figure out what has changed (i.e. which order lines should be added / updated / deleted?).

Questo è un approccio perfettamente ragionevole da prendere - dici all'API come vuoi che la risorsa appaia e l'API deve capire come arrivarci.

Poiché i carrelli degli acquisti in genere non richiedono la collaborazione tra più editor, è possibile ottenere creando un nuovo modello di dati per la risorsa dall'istantanea che è stata fornita, quindi sovrascrivendo il modello esistente.

Se tu, per qualsiasi ragione, hai bisogno di scoprire messaggi di comando con grana più fine dal diff; si, può essere imbarazzante. In un caso come questo, non è male - puoi rilevare il cambiamento abbastanza facilmente, e qualsiasi cambiamento valido è probabilmente estratto da un vocabolario piccolo.

Vale anche la pena notare che non è necessario supportare PUT su alcuna rappresentazione arbitraria della risorsa, ma può invece scegliere e scegliere una rappresentazione che abbia senso (e, si spera, in cui sia facile identificare le modifiche corrette ).

Approccio a grana fine

The delete order line action will result in a DELETE request (DELETE /orders/3/lines/5).

Qualsiasi URI che si desidera utilizzare va bene per quanto riguarda REST; ma concettualmente potrebbe essere meglio pensare in termini di rimozione dell'entità per id, piuttosto che per posizione nell'elenco.

The add order line action will result in a POST request

Potrebbe anche essere fatto come PUT.

This implies the app now needs to keep track of a lot of changes, and execute the changes in the correct order when the user decides to save.

Sì; Credo che ti trovi in una situazione simile con un cliente occasionalmente connesso. I messaggi di comando vengono messi in coda fino a quando il sistema di destinazione non diventa disponibile.

Un altro approccio da considerare sarebbe quello di manipolare la cronologia della risorsa, piuttosto che la risorsa stessa. In effetti, la cronologia diventa un'apposita raccolta di modifiche.

I'm just wondering how I can avoid duplicating business rules on the client. For example, if an order line cannot be deleted under certain circumstances, nothing prevents the client from doing so.

Ci sono un paio di risposte qui, basate sull'idea che segnali le informazioni nelle rappresentazioni.

Una risposta è che annoti semplicemente che la riga dell'ordine non deve essere cancellata. Le regole aziendali che decidono se una riga d'ordine può essere eliminata dal vivo sul server, il client vede solo che il flag è stato impostato.

È possibile comunicare tali informazioni fuori banda ai client.

Nell'approccio a grana fine, la solita risposta è che usi i controlli ipermediali. Piuttosto che i client che inviano un messaggio a un endpoint predeterminato, il client dovrebbe guardare nella rappresentazione e "seguire un collegamento". In questo modo puoi comunicare che non è possibile eliminare un articolo dell'ordine rimuovendo il collegamento dell'elemento di cancellazione dalla rappresentazione.

In un approccio a grana del corso, suppongo che potresti provare a distinguere le parti modificabili della risorsa dal resto e fornire rappresentazioni modificabili solo delle parti che il client è autorizzato a modificare. È probabile che ci sia qualche confusione lì.

    
risposta data 08.02.2017 - 07:43
fonte
1

Consider the following user story: a user retrieves an order. He then deletes one order line and adds 2 more. Finally, he saves the order.

Fondamentalmente, con una modifica e ampli sul lato client avanzata salva , parli di un approccio a grana grossa, in cui il cliente può fare un numero arbitrario di regolazioni e salva il risultato finale. È improbabile che il client si aspetti che ogni passo di modifica lato client venga riprodotto sul server in fase di salvataggio; più probabilmente si aspettano che il risultato finale venga inviato al server.

(Ad esempio, se eseguono inserimenti e cancellazioni degli stessi elementi prima del salvataggio, non ci si aspetta che l'inserimento sia controllato dalle regole aziendali, poiché è stato cancellato prima del salvataggio.)

Molti carrelli degli acquisti non hanno una modifica complessa sul lato client, quindi salvano la funzione, quindi per loro, naturalmente, lavorano su una granularità più fine. Le modifiche del carrello consentono solo il cambio dei conteggi degli elementi pubblicitari, ad esempio. Gli inserimenti di nuovi elementi pubblicitari non si verificano come modifiche e amp; salva il carrello ma come operazione diversa, ad es. da un prodotto (pagina).

L'interfaccia REST deve supportare il comportamento del client desiderato, che sia grossolano, fine (o entrambi con più ui).

    
risposta data 08.02.2017 - 18:52
fonte
1

Se tutto ciò che hai è un martello, tutto sembra un chiodo.

La soluzione al tuo problema potrebbe essere l'uso dei "comandi" (smilar come nella CQRS e pattern CQS ).

POST /api/order/188423
{command: "AddOrderLine", "item_id": 123}

Dopo aver eseguito il POST di questo comando, l'API restituisce la risorsa dell'ordine aggiornata. Questo approccio è più facile da capire e più verificabile. Consente inoltre di eseguire una logica più complessa che interessa molte entità in modo atomico. Puoi anche utilizzare comandi asincroni per calcoli più dispendiosi in termini di tempo.

C'è un articolo scritto da Ali Kheyrollahi che tratta dei modelli menzionati con REST.

Vedi anche il modo RESTful di inviare comandi .

    
risposta data 08.02.2017 - 13:38
fonte
0

Se ora non ci sono regole aziendali sulla rimozione della riga d'ordine, il metodo a grana grossa va bene. Le righe dell'ordine sono solo oggetti valore. Dici "quando più tardi", quando arriva quel momento cambia il tuo modello e possibilmente la tua interfaccia REST.

Il tuo livello applicativo potrebbe tradurre il tuo documento in operazioni contro la collezione order.orderLines, mentre è così che è possibile applicare le regole aziendali.

Il tuo punto 2 mi confonde un po '. Il REST dovrebbe essere apolidi, quindi non si dovrebbe fare "tenere traccia delle modifiche per quando si salva". Conserva l'elemento transitorio sul tuo cliente, invia il risultato completato.

L'interfaccia REST fa parte di un livello applicazione. Non ha bisogno di imitare la semantica del tuo dominio.

L'opzione a grana fine ha forse più senso in una situazione in cui si desidera rimuovere un elemento da un ordine già inviato. Non invierai interi documenti di ordine in questo contesto.

    
risposta data 08.02.2017 - 03:51
fonte

Leggi altre domande sui tag