2009-03-24 9 views
59

Ho visto un sacco di domande relative alla mappatura di DTO per Domain Objects, ma non ho sentito che hanno risposto alla mia domanda. Ho usato molti metodi prima e ho le mie opinioni, ma sto cercando qualcosa di un po 'più concreto.Best practice per il mapping di DTO all'oggetto dominio?

la situazione:

Abbiamo molti oggetti di dominio. Stiamo utilizzando un modello CSLA in modo che i nostri oggetti di dominio possano essere piuttosto complessi e che contengano il proprio accesso ai dati. Non vuoi passare questi in giro sul filo. Scriveremo alcuni nuovi servizi che restituiranno i dati in numerosi formati (.Net, JSON, ecc.). Per questo (e altri motivi) stiamo anche creando un oggetto di trasferimento dati snello da passare sul filo.

La mia domanda è come deve essere collegato l'oggetto DTO e Dominio?

La mia prima reazione è usare un Fowler, DTO pattern-type solution. Ho visto questo fatto molte volte e mi sembra giusto. L'oggetto dominio non contiene riferimenti al DTO. Un'entità esterna (un "mapper" o un "assemblatore") viene chiamata per creare un DTO da un Oggetto Dominio. Normalmente c'è un ORM sul lato dell'oggetto dominio. Lo svantaggio di questo è che il "mapper" tende a diventare estremamente complesso per qualsiasi situazione reale e può essere molto fragile.

Un'altra idea è che l'oggetto dominio "contenga" il DTO, poiché è solo un oggetto dati snello. Le proprietà dell'oggetto dominio farebbero riferimento internamente alle proprietà DTO e potrebbero semplicemente restituire il DTO, se richiesto. Non vedo problemi con questo, ma mi sembra sbagliato. Ho visto alcuni articoli in cui le persone che utilizzano NHibernate sembravano utilizzare questo metodo.

Ci sono altri modi? Vale la pena usare uno dei modi sopra indicati? Se sì o no, perché?

Grazie per eventuali approfondimenti in anticipo.

+2

L'automapper sembra interessante. Ho visto un sacco di codice prima che sarebbe stato sostituito. Il mio problema principale è che se dovessi rimanere bloccato con una tonnellata di codice di mappatura per qualsiasi motivo, preferirei avere il controllo su di esso da solo. –

+2

Quando passiamo da DTO _ a_ Oggetti dominio, quella mappatura è manuale al 100%. È un problema molto più difficile da risolvere, poiché cerchiamo di mantenere operativi i nostri oggetti di dominio, anziché solo i contenitori di dati. Passare a un DTO è un problema facile da risolvere. –

+0

Concordo sul fatto che sia sbagliato che l'oggetto dominio non abbia alcuna conoscenza dell'oggetto dto. Anche se possono essere correlati in questo caso, il loro scopo è completamente separato (i dtos sono generalmente creati per lo scopo) e si creerebbe una dipendenza non necessaria. – Sinaesthetic

risposta

33

Un vantaggio di disporre di un mapper che si trova tra il dominio e il DTO non è così apparente quando si supporta solo una singola mappatura, ma all'aumentare del numero di mapping, avere quel codice isolato dal dominio aiuta a mantenere il dominio più semplice e più snello. Non ingombrerai il tuo dominio con un sacco di peso in più.

Personalmente, cerco di mantenere la mappatura fuori dalle mie entità di dominio e di mettere la responsabilità in quello che chiamo "Livello Manager/Servizio". Questo è un livello che si trova tra l'applicazione e il respository (i) e fornisce la logica di business come il coordinamento del flusso di lavoro (se si modifica A, potrebbe essere necessario modificare anche B in modo che il servizio A funzioni con il servizio B).

Se avessi molti formati finali possibili, potrei cercare di creare un formattatore plug-in che possa usare il pattern Visitor, ad esempio per trasformare le mie entità, ma non ho ancora trovato il bisogno di qualcosa di così complesso.

22

È possibile utilizzare un automapper come the one written by Jimmy Bogard che non ha alcuna connessione tra gli oggetti e fa affidamento sulle convenzioni di denominazione rispettate.

+3

Automapper potrebbe portare a proprietà accidentalmente esposte -> buco di sicurezza. Sarebbe meglio dire esplicitamente cosa dovrebbe essere esposto come DTO. – deamon

+4

@deamon: preoccupazione valida, ma anche i bug (e potenziali buchi di sicurezza a causa della supervisione umana) che possono essere creati scrivendo tutto quel codice di mappatura gooey. Andrò in strada automagic e gestirò il 5% utilizzando la funzione di mappatura personalizzata incorporata. – Merritt

+0

@deamon - non puoi semplicemente fare il mapping condizionale per quelle proprietà che non dovresti esporre? Pensiero AutoMapper gestisce questo scenario? –

1

Un'altra possibile soluzione: http://glue.codeplex.com.

Caratteristiche:

  • bidirezionale mappatura
  • mappatura automatica
  • Mapping tra i diversi tipi
  • mappatura nidificati e l'appiattimento
  • liste e array
  • di verifica delle relazioni
  • Testing i mappatura
  • Proprietà, campi e metodi
5

Usiamo modelli T4 per creare le classi di mappatura.

Pro's - codice leggibile dall'uomo disponibile in fase di compilazione, più veloce di un programma di analisi runtime. Controllo al 100% del codice (può utilizzare metodi parziali/modello di template per estendere funzionalità su base ad-hoc)

Con - escludendo determinate proprietà, raccolte di oggetti di dominio ecc., Imparando la sintassi T4.

0

Posso suggerire uno strumento che ho creato ed è open source ospitato su CodePlex: EntitiesToDTOs.

La mappatura dal DTO all'entità e viceversa è implementata mediante metodi di estensione, questi compongono il lato Assemblatore di ciascuna estremità.

si finisce con il codice come:

Foo entity = new Foo(); 
FooDTO dto = entity.ToDTO(); 
entity = dto.ToEntity(); 

List<Foo> entityList = new List<Foo>(); 
List<FooDTO> dtoList = entityList.ToDTOs(); 
entityList = dtoList.ToEntities(); 
+0

questo è architettonicamente sbagliato perché rendi conto DTO e le entità di dominio a conoscenza l'uno dell'altro. – Raffaeu

+5

@Raffaeu Non credo perché i metodi ToDTO/ToDTOs/ToEntity/ToEntities sono definiti come metodi di estensione che rappresentano gli Assembler. La logica di convertire un'entità in un DTO e viceversa è nei metodi di estensione (Assembler), non nell'entità/DTO in effetti. – kzfabi

+2

Se parli di "Assembler", implementalo in modo corretto. Rendili modulari, rendili facilmente intercambiabili, usa l'iniezione di dipendenza. Non è necessario che lo stesso modello di dominio sia a conoscenza di una conversione in DTO. Diciamo che ho 1 oggetto dominio ma 50 diverse applicazioni che usano lo stesso dominio, ognuna con il proprio DTO. Non hai intenzione di creare 50 estensioni. Invece creerai un servizio applicativo per ogni applicazione con l'assemblatore (i) necessario (i) che viene iniettato come dipendenza dal servizio. –

1

Come vedi ad implementare un costruttore all'interno della classe DTO che prende come parametro un oggetto di dominio?

dire ... Qualcosa di simile

class DTO { 

    // attributes 

    public DTO (DomainObject domainObject) { 
      this.prop = domainObject.getProp(); 
    } 

    // methods 
} 
+6

Per favore, non farlo mai. Non vuoi che il tuo livello DTO sia consapevole o dipendente dal tuo livello di dominio. Il vantaggio della mappatura è che gli strati inferiori possono essere facilmente sostituiti modificando la mappatura, o che le modifiche nel livello inferiore possono essere controllori modificando la mappatura. Diciamo che dtoA esegue il mapping a domainObjectA oggi, ma domani il requisito è che si associ a domainObjectB. Nel tuo caso devi modificare l'oggetto DTO, che è un grande no-no. Hai perso molti benefici del mappatore. –

+1

Prima di tutto, grazie! : D. Quindi @FrederikPrijck inserendo un livello tra 'DTO' e' DomainObject', tendiamo essenzialmente a risolvere questo problema del DTO sull'oggetto dominio, quindi tutto il "lavoro di costruzione" viene fatto in un middle layer (classe) chiamato ' mapper', che dipende da DTO e DomainObjects. Quindi è meglio, o generalmente raccomandare, un approccio a questo problema? Chiedo solo di assicurarmi che il punto sia stato capito. – Victor

+4

Sì, il livello si chiama "Assembler". Utilizzando un terzo livello per definire i mapping, è possibile sostituire facilmente il layer assembler con un'altra implementazione (ad es. Rimuovere Automapper e utilizzare mappature manuali), che è sempre una scelta migliore. Il modo migliore per capirlo è pensare a dove ti darei l'oggetto A, e qualcun altro ti dà l'oggetto B. Non hai accesso a ciascuno di questi oggetti (solo dll), quindi la mappatura può essere fatta solo creando un terzo strato. Ma anche se è possibile accedere a uno qualsiasi degli oggetti, le mappature dovrebbero sempre essere eseguite all'esterno, poiché non sono correlate. –

0

Perché non possiamo fare in questo modo?

class UserDTO { 
} 

class AdminDTO { 
} 

class DomainObject { 

// attributes 
public DomainObject(DTO dto) { 
     this.dto = dto; 
}  

// methods 
public function isActive() { 
     return (this.dto.getStatus() == 'ACTIVE') 
} 

public function isModeratorAdmin() { 
     return (this.dto.getAdminRole() == 'moderator') 
} 

} 


userdto = new UserDTO(); 
userdto.setStatus('ACTIVE'); 

obj = new DomainObject(userdto) 
if(obj.isActive()) { 
    //print active 
} 

admindto = new AdminDTO(); 
admindto.setAdminRole('moderator'); 

obj = new DomainObject(admindto) 
if(obj.isModeratorAdmin()) { 
    //print some thing 
} 

@FrederikPrijck (o) qualcuno: Si prega di suggerire. Nell'esempio sopra DomainObject dipende da DTO. In questo modo posso evitare che il codice esegua il mapping di dto < -> domainobject.

o la classe DomainObject può estendere la classe DTO?