Domanda

Ho un modello di dominio ricco, dove la maggior parte le classi hanno alcuni comportamenti e alcune proprietà che sono o calcolati o esporre le proprietà degli oggetti membri (che equivale a dire che i valori di queste proprietà non sono mai persistenti).

Il mio cliente dice al server solo tramite WCF.

Come tale, per ogni entità dominio, ho un corrispondente DTO - una semplice rappresentazione che contiene solo dati - così come una classe mapper che implementa DtoMapper<DTO,Entity> e può convertire un'entità nel suo equivalente DTO o viceversa attraverso un gateway statica:

var employee = Map<Employee>.from_dto<EmployeeDto>();

Il lato server di questa applicazione è principalmente circa la persistenza, dove i miei DTOs entrano dal servizio WCF, sono deserializzata, e poi un ORM arbitrario loro persiste al database, o di una richiesta di query arriva da WCF e le esegue ORM che query sul DB e restituisce oggetti da serializzare e rispediti da WCF.

Dato questo scenario, Non mi ha alcun senso per mappare il mio di persistenza per le entità del dominio, o devo solo mappare direttamente alle DTOS?

Se io uso entità del dominio, il flusso sarebbe

  1. le richieste dei client object
  2. WCF trasmette la richiesta al server
  3. ORM query di database e restituisce entità del dominio
  4. entità del dominio trasformati in DTOs da mapper
  5. WCF serializza DTO e ritorna al cliente
  6. client deserializza DTO
  7. DTO trasformato in un'entità dominio mapper
  8. ViewModels creati, ecc.

simile al viaggio di ritorno

Se traccio dritto al DTO, posso eliminare una mappatura per oggetto, per ogni richiesta. Quello che faccio a perdere in questo modo?

L'unica cosa che viene in mente è un'altra opportunità per convalidare prima di inserimento / aggiornamento, perché non ho alcuna garanzia che il DTO è stato mai oggetto di validazione o addirittura esisteva come entità dominio prima di essere inviati attraverso il filo, e immagino la possibilità di validare il select (se un altro processo potrebbe aver messo valori non validi nel database). Ci sono altre ragioni? Sono queste ragioni sufficienti a giustificare i passi di mappatura aggiuntive?

modifica:

L'ho fatto dire "arbitrario ORM" di cui sopra, e io voglio che le cose siano come ORM-e-la persistenza agnostico possibile, ma se avete qualcosa di speciale da aggiungere che è specifico per NHibernate, con tutti i mezzi fanno.

È stato utile?

Soluzione

Io personalmente consigliamo mantenere la mappatura sul lato server. Probabilmente avete fatto un sacco di lavoro costruire il vostro disegno al punto che è a oggi; non buttare via tutto.

Si consideri ciò che un servizio web è. E non è solo un'astrazione sopra la vostra ORM; si tratta di un contratto . Si tratta di un'API pubblica per i vostri clienti, sia interni che esterni.

Un API pubblica dovrebbe avere poco o nessun motivo di cambiare. Quasi qualsiasi modifica un'API, a parte l'aggiunta di nuovi tipi e metodi, è un cambiamento di rottura. Ma il vostro modello di dominio non sta per essere così rigoroso. Sarà necessario cambiare di volta in volta quando si aggiungono nuove funzioni o scoprire i difetti nel disegno originale. Si vuole essere in grado di garantire che le modifiche al modello interno non causano cascata cambiamenti attraverso il contratto del servizio.

In realtà è una pratica comune (non voglio insultare i lettori con la frase "best practice") per creare le classi Request e Response specifici per ogni messaggio per un motivo simile; diventa molto più semplice per estendere la capacità dei servizi e dei metodi esistenti senza che esse siano cambiamenti di rottura.

I clienti probabilmente non vogliono esattamente lo stesso modello che si utilizza internamente il servizio. Se siete il vostro unico cliente, allora forse questo sembra trasparente, ma se si dispone di client esterni e avete visto fino a che punto la loro interpretazione del sistema può essere spesso, poi capirete il valore di non permettere il vostro modello ideale per perdite fuori i confini della API di servizio.


E a volte, non è nemmeno possibile per inviare il modello indietro attraverso l'API. Ci sono molte ragioni per cui questo può accadere:

  • cicli nel grafico oggetto. Perfettamente bene in OOP; disastroso serializzazione. Si finisce per dover fare scelte permanenti dolorose di cui "direzione" del grafico deve essere serializzato. D'altra parte, se si utilizza un DTO, è possibile serializzare in qualsiasi direzione si vuole, comunque sia il compito a portata di mano.

  • Il tentativo di utilizzare determinati tipi di meccanismi di ereditarietà SOAP / resto può essere un kludge al meglio. Il serializzatore XML vecchio stile almeno sostiene xs:choice; DataContract non lo fa, e non voglio cavillare su logica, ma basti dire che probabilmente hai qualche polimorfismo all'interno del ricco modello di dominio ed è dannatamente quasi impossibile da incanalare che attraverso il servizio web.

  • lazy loading / differite, che probabilmente si fanno uso di se si utilizza un ORM. E 'sufficiente fare imbarazzante assicurarsi che viene serializzato correttamente - ad esempio, utilizzando LINQ to SQL entità, WCF non ha nemmeno scattare il caricatore pigro, sarà appena messo null in quel campo a meno che non si carica manualmente - ma il problema diventa ancora peggiore per i dati a tornare in qualcosa di semplice come un auto-immobile List<T> che è inizializzata nel costruttore -. abbastanza comune in un modello di dominio - semplicemente non funziona in WCF, perché non richiama il vostro costruttore. Invece è necessario aggiungere un metodo [OnDeserializing] inizializzatore, e davvero non vogliono ingombrare il vostro modello di dominio con questa immondizia.

  • Inoltre ho appena notato l'osservazione parentesi che si utilizza NHibernate. Si consideri che le interfacce come IList<T> non possono essere serializzati in tutto un servizio web! Se si utilizzano le classi POCO con NHibernate, come la maggior parte di noi, allora questo semplicemente non funziona, punto.


Ci saranno anche probabilmente molti casi in cui il modello di dominio interno semplicemente non corrisponde alle esigenze del cliente, e non ha senso cambiare il vostro modello di dominio di soddisfare tali esigenze. Come esempio di questo, prendiamo qualcosa di semplice come una fattura. Ha bisogno di mostrare:

  • Informazioni sul conto (numero di conto, nome, ecc.)
  • i dati (numero di fattura, data, scadenza, specifiche per fattura etc.)
  • Informazioni A / R-level (precedente equilibrio, spese ritardate, nuovo equilibrio)
  • Prodotto o servizio di informazioni per tutto ciò che sulla fattura;
  • Etc.

Questo si inserisce probabilmente bene all'interno di un modello di dominio. Ma cosa succede se il cliente vuole eseguire un report che mostra 1200 di queste fatture? Una sorta di rapporto di riconciliazione?

Questo fa schifo per la serializzazione. Ora si sta inviando 1200 fatture con stessi dati che vengono serializzati più e più volte - stessi conti, stessi prodotti, la stessa A / R. Internamente, l'applicazione tiene traccia di tutti i collegamenti; conosce la fattura # 35 e # 45 sono fattura per lo stesso cliente e quindi condividono un riferimento Customer; tutte queste informazioni è perso su di serializzazione e si finisce per l'invio di una quantità ridicola di dati ridondanti.

Che cosa si vuole veramente è quello di inviare un rapporto personalizzato che comprende:

  • Tutti gli account inclusi nel rapporto, e il loro A / R;
  • Tutti i prodotti inclusi nella relazione;
  • Tutte le fatture, con prodotto e ID account solo.

È necessario eseguire ulteriore "normalizzazione" sui vostri dati in uscita prima di inviarlo al cliente, se si vuole evitare la ridondanza di massa. Ciò favorisce fortemente l'approccio DTO; non ha senso avere questa struttura nel vostro modello di dominio perché il vostro modello di dominio già si prende cura di esuberi, a suo modo.

Spero che questi sono abbastanza esempi e abbastanza logica per convincerti a mantenere le mappature dal dominio <-> contratto di assistenza intatto. Hai fatto assolutamente la cosa giusta fino ad ora, si dispone di un grande disegno, e sarebbe un peccato per negare tutto quello sforzo in favore di qualcosa che potrebbe portare a gravi mal di testa in seguito.

Altri suggerimenti

Sarà necessario mappare i DTOs del lato client in ogni caso, quindi, per la simmetria, è meglio fare la mappatura inversa nel lato server. In questo modo si isolare le conversioni in livelli di astrazione ben separati.

livelli di astrazione sono buoni non solo per le convalide, ma per isolare il codice da modifiche di sotto / sopra di esso, e rendere il codice più verificabili e con meno ripetizioni.

Inoltre, se non si nota un grande collo di bottiglia nella conversione in più, ricordate: l'ottimizzazione precoce è la radice di ogni male. :)

Si dovrebbe tenere le entità del dominio separato dal DTO sono diverse preoccupazioni. DTO sono di solito heriachal, modelli di auto-descrive dove come i tuoi entità del dominio invece incapsulare la logica di business e hanno un sacco di comportamento attaccato con loro.

Detto questo non sono sicuro dove la mappatura extra è? Si recuperano i dati utilizzando il tuo ORM (aka entità del dominio) e mappare gli oggetti al vostro DTO quindi c'è solo 1 mappatura lì? BTW se non sei già usa qualcosa come Automapper a fare la mappatura noioso per voi.

Gli stessi DTO vengono poi deserializzata sul cliente e da lì si può mappare direttamente ai vostri UIViewModels. Così il quadro generale sembra qualcosa di simile:

  • Client richiede un'entità da Id dal servizio WCF
  • WCF Servizio ottiene un'entità da Repository / ORM
  • Utilizza un automapper per mappare da entità DTO
  • Cliente riceve DTO
  • Utilizza un automapper per mappare l'interfaccia utente ViewModel
  • UIViewModel è binded alla GUI

Quando si dice che la vostra applicazione lato server è "in gran parte" circa la persistenza, penso che questa sia la cosa fondamentale a cui pensare. C'è davvero un modello di dominio lato server che richiede un po 'di intelligenza intorno i dati che riceve o fa il vostro servizio WCF puramente agisce come gateway tra il modello di dominio e l'archivio dati?

Inoltre, considerare se il vostro DTO è progettato per il dominio client.
È questo l'unico dominio cliente che ha bisogno di accedere a tale archivio di dati tramite il vostro servizio?
Sono i DTOs lato server flessibile o grana grossa sufficiente a servire un dominio applicazione diverso?
Se no, allora è probabilmente vale la pena di tenere le implementazioni di interfacce esterne astratti.

  

(DB-> ORM-> EmployeeEntity-> Client1DTOAssembler-> Client1EmployeeDTO).

Abbiamo un'applicazione simile dove un servizio WCF agisce principalmente come un gateway per l'archivio di dati persistenti.

Nel nostro caso, il nostro client e server non riutilizzare il gruppo contenente "DTOs." Questo ci dà l'opportunità di aggiungere semplicemente il codice per le classi parziali generati dal riferimento al servizio, così spesso sono in grado di utilizzare un DTO così come sono sul lato client e trattarla come un oggetto di dominio. Altre volte possono avere gli oggetti sul lato client di dominio di sola che servono come facciate a un gruppo di oggetti persistenti che abbiamo ottenuto dal servizio WCF.

Quando si pensa circa i comportamenti e le proprietà calcolate che gli oggetti del dominio hanno, quanta sovrapposizione è lì, davvero tra il client e il server? Nel nostro caso, abbiamo stabilito che la ripartizione delle responsabilità tra client e server significava che c'era molto poco, se del caso, il codice che aveva bisogno di essere presente (ed esattamente la stessa) sia sul client e server.

Per rispondere alle vostre domande direttamente, se il vostro obiettivo è quello di rimanere completamente la persistenza agnostico, avrei certamente mappare il vostro negozio di persistenza per gli oggetti del dominio e quindi mappare DTOS. Ci sono troppi implementazioni di persistenza che possono sanguinare in oggetti e complicare il loro utilizzo come WCF DTOs.

Sul lato client, potrebbe non essere necessario fare una mappatura aggiuntiva se si può semplicemente decorare o aumentare le tue DTOs e questo è una soluzione abbastanza semplice.

Il tuo architettura sembra abbastanza ben pensato. Il mio intestino-senso è, se hai già deciso di ridurre gli oggetti a DTO di inviare loro attraverso WCF, e attualmente non hanno bisogno di funzionalità oggetto supplementare sul lato server, perché non mantenere le cose semplici e mappa il vostro negozio di persistenza direttamente ai DTOs.

Che cosa si perde? Non credo che si ha realmente perde nulla. Sei architettura è pulito e semplice. Se si decide nel futuro che c'è un nuovo bisogno di funzionalità più avanzate sul lato server, si può sempre ri-fattore in quel punto per ricreare i tuoi entità del dominio lì.

mi piace fare cose semplici e ri-fattore, se necessario in seguito, cercare di evitare la cosa ottimizzazione prematura, ecc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top