Le API RESTful tendono a incoraggiare modelli di dominio anemici?


34

Sto lavorando a un progetto in cui stiamo cercando di applicare sia la progettazione guidata dal dominio sia il REST a un'architettura orientata ai servizi. Non ci preoccupiamo del 100% di conformità REST; probabilmente sarebbe meglio dire che stiamo cercando di costruire API HTTP orientate alle risorse (~ Livello 2 del modello di maturità REST di Richardson). Tuttavia, stiamo cercando di stare lontano dall'uso in stile RPC delle richieste HTTP, ovvero tentiamo di implementare i nostri verbi HTTP secondo RFC2616 piuttosto che usare POSTper fare IsPostalAddressValid(...), per esempio.

Tuttavia, un'enfasi su questo sembra essere a spese del nostro tentativo di applicare la progettazione guidata dal dominio. Con un solo GET, POST, PUT, DELETEe un paio di altri metodi usati raramente, si tende a costruire servizi cruddy e servizi cruddy tendono ad avere modelli di dominio anemici.

POST: Ricevere i dati, convalidarli, scaricarli sui dati. GET: Recupera i dati, restituiscili. Nessuna vera logica di business lì. Usiamo anche messaggi (eventi) tra i servizi e mi sembra che la maggior parte della logica aziendale finisca per essere costruita attorno a quello.

REST e DDD sono in tensione ad un certo livello? (O sto fraintendendo qualcosa qui? Forse stiamo sbagliando qualcos'altro?) È possibile costruire un forte modello di dominio in un'architettura orientata ai servizi evitando le chiamate HTTP in stile RPC?


1
Il POST è stato deliberatamente progettato per essere "intenzionalmente vago;" il risultato di un POST è specifico dell'implementazione. Cosa ti impedisce di fare ciò che fanno Twitter e altri progettisti di API e definire ogni metodo POST nella parte non CRUD della tua API in base ai tuoi requisiti specifici?
Robert Harvey,

@RobertHarvey Abbiamo creato POST come una creazione. Guardando di nuovo allo standard , forse è troppo semplicistico. Ad esempio, pensi che il POST al fine di IsPostalAddressValid(...)adattarsi a "Fornire un blocco di dati, come il risultato dell'invio di un modulo, a un processo di gestione dei dati"?
Kazark

Questo perché non esiste un verbo CREATE (e nessun verbo UPDATE, del resto). Sostengo che quei verbi mancano dallo standard (per qualsiasi motivo), motivo per cui devi cooptare POST per "tutto il resto". Il tuo "processo di gestione dei dati", in questo caso, è il processo che esamina l'indirizzo postale e restituisce un valore corrispondente al risultato di tale analisi.
Robert Harvey,

1
@RobertHarvey: credo che POST e PUT / PATCH sia semplicemente il verbo CREATE e UPDATE che stavi desiderando. È chiamato solo in modo diverso in modo che il verbo abbia ancora un senso anche nel design non RESTful.
Sdraiati Ryan il

@LieRyan: te lo concedo. Penso solo che CRUD implichi per definizione modelli di dati anemici. Puoi portare un certo comportamento se, diciamo, ti trovi nella M di MVC, ma certamente non attraverso sistemi eterogenei. Per tutto il resto tranne CRUD, è necessario il POST.
Robert Harvey,

Risposte:


38

La prima legge dei sistemi distribuiti di Martin Fowler: "Non distribuire i tuoi oggetti!" Le interfacce remote devono essere a grana grossa e le interfacce interne a grana fine. Spesso il modello di dominio avanzato si applica solo in un contesto limitato .

L'API REST separa due contesti diversi, entrambi con i propri modelli interni. I contesti comunicano attraverso un'interfaccia a grana grossa (API REST) ​​utilizzando oggetti "anemici" (DTO).

Nel tuo caso sembra che tu stia cercando di diffondere un contesto su un limite che è l'API REST. Ciò può comportare un'interfaccia remota o un modello anemico a grana fine. A seconda del progetto potrebbe essere o meno un problema.


1
Fowler ha molti buoni pensieri, ma non dimentichiamo che disastro sono state le specifiche e le implementazioni EJB originali. È stato solo dopo che hanno capito che le chiamate di metodo di basso livello per ogni operazione minore come getName () era un incubo di rete / carico. Le interfacce a grana grossa sono diventate la strada da percorrere e con essa il concetto che interi grafici / messaggi di entità sono stati inviati e ricevuti nel contesto verbo + sostantivo.
Darrell Teague,

9

Il POST è stato deliberatamente progettato per essere "intenzionalmente vago;" il risultato di un POST è specifico dell'implementazione. Cosa ti impedisce di fare ciò che fanno Twitter e altri progettisti di API e definire ogni metodo POST nella parte non CRUD della tua API in base ai tuoi requisiti specifici? POST è il verbo catchall. Usalo quando nessuno degli altri verbi si adatta bene all'operazione che vuoi eseguire.

Per dirla in altro modo, la tua domanda potrebbe essere posta allo stesso modo in quanto "Gli oggetti 'intelligenti' incoraggiano il design in stile RPC?" Anche Martin Fowler (che ha coniato il termine "Anemic Domain Model") ammette che i DTO nudi hanno alcuni vantaggi:

Inserire il comportamento negli oggetti del dominio non dovrebbe contraddire il solido approccio dell'utilizzo della stratificazione per separare la logica del dominio da cose come la persistenza e le responsabilità di presentazione. La logica che dovrebbe trovarsi in un oggetto dominio è la logica del dominio - convalide, calcoli, regole aziendali - come preferisci chiamarlo.

Per quanto riguarda il modello di maturità Richardson , puoi arrivare al livello 3 senza mai preoccuparti di "Anemic Domain Models". Ricorda, non trasferirai mai il comportamento nel browser (a meno che non preveda di iniettare Javascript attraverso i tuoi modelli).

REST riguarda principalmente l'indipendenza della macchina; implementare il modello REST nella misura in cui si desidera che gli endpoint rappresentino le risorse e che i consumatori dell'API possano accedere e mantenere facilmente tali risorse in modo standard. Se questo sembra anemico, allora così sia.

Vedi anche
Ho bisogno di più verbi


Penso che certamente affronti il ​​lato RFC2616 della domanda. Che dire del fatto che stiamo cercando di essere orientati alle risorse, vale a dire almeno a raggiungere il livello 2 nel modello di maturità di Richardson per REST?
Kazark,

1
Ho letto su martinfowler.com/articles/richardsonMaturityModel.html . Puoi arrivare al livello 3 senza mai preoccuparti di "Anemic Domain Models". Ricorda, non trasferirai mai il comportamento nel browser (a meno che non preveda di iniettare Javascript attraverso i tuoi modelli).
Robert Harvey,

4

L'API REST è solo un tipo di livello di presentazione. Non ha nulla a che fare con il modello di dominio.

La domanda che hai postato deriva dalla tua confusione che in qualche modo devi adattarti l'uno all'altro. Non

Mappa il tuo modello di dominio sull'API REST nello stesso modo in cui mappi il tuo modello di dominio su un RDBMS tramite un ORM: deve esserci questo livello di mappatura.

Dominio ← ORM →
Dominio RDBMS ← Mappatura REST → API REST


3

IMHO Non penso che tendano a incoraggiare i modelli di dominio anemico (ADM), ma richiedono che tu ti prenda del tempo e rifletta.

Prima di tutto, penso che la caratteristica principale degli ADM sia che hanno poco o nessun comportamento in essi. Questo non vuol dire che il sistema non ha alcun comportamento, solo che di solito è in una sorta di classe di servizio (vedi http://vimeo.com/43598193 ).

E ovviamente se il comportamento non esiste nell'ADM, che cosa fa? La risposta ovviamente è i dati. E quindi come si associa all'API REST? Bene presumibilmente, i dati sono mappati sul contenuto della risorsa e il comportamento è mappato sui verbi HTTP.

Quindi hai tutto ciò di cui hai bisogno per creare un modello di dominio avanzato, devi solo essere in grado di vedere come i verbi HTTP si associano alle operazioni di dominio sui dati e quindi inserire tali operazioni nelle stesse classi che incapsulano i tuoi dati.

Penso che il punto in cui le persone tendono a incorrere in problemi sia che hanno difficoltà a vedere come i verbi HTTP si associano al comportamento del loro dominio quando il comportamento è al di là del semplice CRUD, cioè quando ci sono effetti collaterali in altre parti del dominio oltre il risorsa modificata dalla richiesta HTTP. Un modo per risolvere questo problema è con gli eventi di dominio ( http://www.udidahan.com/2009/06/14/domain-events-salvation/ ).


3

Questo articolo è abbastanza correlato all'argomento e credo che risponda alla tua domanda.

Un concetto chiave che penso risponda molto bene alla tua domanda, è sintetizzato nel seguente paragrafo dall'articolo citato:

"È molto importante distinguere tra risorse nell'API REST ed entità di dominio in una progettazione guidata dal dominio. La progettazione guidata dal dominio si applica al lato dell'implementazione (inclusa l'implementazione dell'API) mentre le risorse nell'API REST guidano la progettazione e il contratto dell'API. Risorse API la selezione non dovrebbe dipendere dai dettagli di implementazione del dominio sottostante. "


1

Diverse implementazioni di discreto successo che ho visto / costruito rispondono alla domanda su come mescolano il verbo + la metafora del sostantivo usando metodi "business friendly" a grana grossa che agiscono sulle entità.

Quindi, invece del getName()metodo / servizio (condannato) , esporre getPerson(), passando cose come identificatore-tipo / ID, restituendo l'intera Personentità.

Poiché i comportamenti dell'entità Persona in un tale contesto non possono essere adeguatamente comunicati (né forse dovrebbero essere in un contesto incentrato sui dati come questo), è perfettamente ragionevole definire un modello di dati (rispetto all'oggetto) per le coppie richiesta / risposta di il / i servizio / i.

I servizi e i verbi definiti aggiungeranno alcuni comportamenti, controlli e persino regole di transizione dello stato consentite dal dominio per le entità. Ad esempio, ci sarebbe una logica specifica del dominio su ciò che accade nella transferPerson()chiamata di servizio, ma l'interfaccia stessa definirebbe solo input / output entità / dati senza definire i LORO comportamenti interni.

Non sarei d'accordo con gli autori che direbbero, ad esempio, che un'implementazione del verbo di trasferimento appartiene alla classe Person o associata a un servizio incentrato sulla persona. In effetti, il metodo di trasferimento per Personae sue opzioni (in questo semplice esempio) sarebbe meglio definito da a Carrier, in cui Personpotrebbe non essere a conoscenza nemmeno di quali metodi di trasferimento siano disponibili o di come avvenga anche il trasferimento (chissà come funzionano i motori a reazione Comunque).

Questo rende l' Personentità anemica? Io non la penso così.

Può esserci / dovrebbe esserci una logica su cose specifiche della Persona che sono interne alla Persona come il loro stato di salute, che non dovrebbe essere definito da una classe esterna.

Tuttavia, a seconda dei casi d'uso, è del tutto accettabile che una classe di entità non abbia comportamenti importanti / rilevanti in alcuni sistemi, come un servizio di assegnazione di posti in un sistema di trasporto. Un tale sistema può ben implementare servizi basati su REST che trattano istanze Person e identificatori associati ma non definiscono / implementano mai i loro comportamenti interni.


Aspetti positivi --- questo porta davvero una nuova prospettiva che altre risposte non avevano ancora.
Kazark,

0

Il tuo problema è che stai cercando di inserire il tuo modello nel set base di verbi, usando POST il più possibile?

Non è necessario - so che per la maggior parte delle persone REST significa POST, GET, PUT e DELETE, ma il http rfc dice:

L'insieme di metodi comuni per HTTP / 1.1 è definito di seguito. Sebbene questo set possa essere espanso, non è possibile assumere ulteriori metodi per condividere la stessa semantica per client e server estesi separatamente.

E sistemi come SMTP usano lo stesso stile di metodi basati sui verbi ma con un set totalmente diverso.

Quindi, non c'è motivo per cui devi usarli, puoi usare qualsiasi set di verbi che ti piace (anche se, davvero, scoprirai che puoi fare tutto ciò di cui hai bisogno nel 4 di base con un po 'di pensiero). La cosa che distingue REST dagli altri meccanismi è il suo modo apolide e coerente di implementare questi verbi. Non dovresti provare a implementare il sistema di passaggio dei messaggi tra i livelli poiché in pratica non stai eseguendo REST, stai invece facendo un meccanismo di passaggio dei messaggi, RPC o coda dei messaggi, che senza dubbio ti perderà i vantaggi di REST (ovvero semplicità che lo fa funzionare davvero bene su una connessione http).

Se si desidera un protocollo di messaggistica complesso e completo, quindi costruirlo (se è possibile farlo sul Web, c'è un motivo per cui REST è così popolare), ma altrimenti provare a attenersi al design architettonico di REST.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.