È buona norma utilizzare oggetti entità come oggetti di trasferimento dati?


29

Mi chiedo perché, se lo è, perché Entity Framework non offre la logica per creare un nuovo oggetto con le stesse proprietà per trasferire i dati tra i livelli?

Uso gli oggetti entità generati con il framework entità.


3
Penso che questa sia una buona domanda, ma non posso davvero dirlo perché è così difficile da leggere, non sono sicuro di come risolverlo. Modifica la tua domanda.
candied_orange,

1
@CandiedOrange +1, e rende ancora più spaventoso il fatto che questo abbia ottenuto così tanti voti.
guillaume31,

Risposte:


23

Spetta a voi.

Molte persone ti diranno che non è una buona pratica, ma in alcuni casi puoi cavartela.

EF non ha mai giocato bene con DDD per diversi motivi, ma due spiccano: non puoi avere costruttori parametrizzati sulle tue entità e non puoi incapsulare raccolte. DDD si basa su questo, poiché il modello di dominio dovrebbe includere sia dati che comportamento.

In un certo senso, EF ti costringe ad avere un modello di dominio anemico e in questo caso puoi usare le entità come DTO. Potresti riscontrare alcuni problemi se usi le proprietà di navigazione ma puoi serializzare quelle entità e inviarle via cavo. Potrebbe non essere pratico però. Dovrai controllare la serializzazione per ogni entità che ha proprietà che non devi inviare. Il modo più semplice è semplicemente progettare classi separate su misura per il trasferimento dei dati. Le librerie come AutoMapper sono create per questo scopo.

Ad esempio: supponiamo di avere una classe chiamata Personcon la seguente definizione:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; get; }

    // plus a bunch of other properties relevant about a person
}

Supponendo che si desidera visualizzare un elenco di dipendenti da qualche parte, può essere pratico di inviare solo il Id, FirstNamee LastName. Ma dovrai inviare tutte le altre proprietà irrilevanti. Non è un grosso problema se non ti interessa la dimensione della risposta, ma l'idea generale è quella di inviare solo i dati rilevanti. D'altra parte, è possibile progettare un'API che restituisce un elenco di persone e in tal caso potrebbe essere necessario inviare tutte le proprietà, quindi ha senso serializzare e inviare le entità. In questo caso, la creazione di una classe DTO è discutibile. Ad alcune persone piace mischiare entità e DTO, altre no.

Per rispondere alla tua domanda aggiornata, EF è un ORM. Il suo compito è mappare i record del database su oggetti e viceversa. Quello che fai con quegli oggetti prima e dopo aver attraversato EF non fa parte delle sue preoccupazioni. Né dovrebbe essere.


1
Riassumendo brevemente. Quali sono le differenze tra DTO e POCO? Non trattengono solo i dati?
Laiv

1
@ guillaume31 Non puoi avere un vero modello di dominio senza prendere in considerazione la persistenza. A che serve un'interfaccia pubblica se sono costretto a usare la riflessione (per non parlare di altri tipi di hack, a condizione che la mia proprietà pubblica sia supportata da un campo privato chiamato in modo diverso) per caricare i miei aggregati in uno stato valido? Se EF non mi sta fornendo un'infrastruttura per idratare i miei aggregati utilizzando la loro interfaccia pubblica, allora farò meglio a mantenere la mia logica di business altrove e avere il mio dominio anemico invece di scrivere un'ulteriore piastra di caldaia per caricarli dal database.
Devnull,

1
Preferiresti rendere anemico tutto il tuo modello di dominio piuttosto che nominare la tua raccolta pubblica come il campo privato? (mettiamo da parte il problema del costruttore, perché in primo luogo è più spesso contro il dominio esporre un costruttore che prende tutti i campi dell'oggetto ...)
guillaume31

1
@Konrad da qui : As of EF Core 2.1, only services known by EF Core can be injected. Support for injecting application services is being considered for a future release.mi piace molto avere servizi di applicazione iniettati nelle mie entità.
Devnull,

1
@Konrad Smetterei di usare le mie entità di dominio come DTO (se dovessi usarle in questo modo). I DTO sono utili indipendentemente dal supporto di EF per l'iniezione del servizio.
devnull

8

No non lo è.

Idealmente, i DTO corrisponderanno ai repository di persistenza (ovvero le tabelle del database).

Ma le tue business class non sono necessariamente una partita. Potresti aver bisogno di classi aggiuntive, o classi separate o unite a ciò che hai nel database. Se la tua applicazione è piccola, potresti non vedere davvero questo tipo di problemi, ma in applicazioni medio-grandi, questo accadrà spesso.

Un'altra cosa è che i DTO fanno parte del dominio di qualsiasi cosa si occupi di persistenza, mentre il tuo livello aziendale non dovrebbe sapere nulla di loro.


Un oggetto Command è un DTO di cui il livello aziendale dovrebbe sapere qualcosa.
Devnull,

Sto pensando che il livello aziendale probabilmente chiamerà l'oggetto comando indirettamente, attraverso un'interfaccia. E intendo DTO come nei semplici oggetti che trasportano dati, senza alcuna funzionalità. Non sono sicuro che un oggetto comando sia un DTO ( martinfowler.com/eaaCatalog/dataTransferObject.html ).
Juan Carlos Eduardo Romaina Ac,

1
Di solito, i comandi sono separati nel comando effettivo e nel suo gestore. Il comando contiene i dati, il gestore contiene il comportamento. Se utilizzata in questo modo, la parte del comando contiene solo i dati ricevuti dal client e funge da DTO tra il client e il livello aziendale.
Devnull,

DTO non scorre dalla persistenza al dominio, potrebbe anche provenire dall'esterno come nei dati in arrivo (si pensi alle API REST). Come indicato da devnull, i comandi e gli eventi sono in qualche modo DTO e vengono trasferiti al livello aziendale. Se i gestori sono di livello aziendale o no dipende dal design.
Laiv

4

In realtà è una pessima idea. Martin Fowler ha un articolo sui DTO locali .

Per farla breve, DTOPattern è stato utilizzato per il trasferimento di dati all'esterno del processo, ad esempio via cavo e non tra i livelli all'interno dello stesso processo.


Come per tutti gli approcci di programmazione, non esiste una dimensione adatta a tutti altrimenti non discuteremmo e discuteremo di schemi e approcci diversi in ogni applicazione che costruiamo, useremmo sempre le stesse cose. I DTO sono essenzialmente solo POPO a cui sono stati appena chiamati per mostrare un certo intento. Non c'è niente con l'uso di un DTO per "trasferire dati" tra ad esempio servizio, controller e in vista. Non c'è nulla nel nome o nella struttura delle classi che indichi o limiti da dove e verso i dati vengono trasferiti
James,

4

No, è una cattiva pratica.

Alcuni motivi:

  1. I nuovi campi entità saranno nel Dto, per impostazione predefinita . Utilizzare l'entità significa che tutte le informazioni saranno disponibili per essere consumate per impostazione predefinita. Questo può portarti a esporre informazioni sensibili o, almeno, a gonfiare il tuo contratto API, con molte informazioni che non vengono utilizzate per chi consuma l'API. Certo, puoi ignorare i campi usando alcune annotazioni (come @JsonIgnoredal mondo Java), ma questo porta al prossimo problema ...
  2. Molte annotazioni / condizioni / controlli sull'entità . È necessario controllare ciò che si desidera inviare su Dto, quando viene modificato il nome di un attributo (ciò interromperà il contratto), aggiungere alcuni attributi che non provengono dall'entità, dall'ordine degli attributi, ecc. Presto vedrai il tuo semplice entità con molte annotazioni, campi extra e ogni volta sarà più difficile capire cosa sta succedendo.
  3. Meno flessibilità . Con un Dto sei libero di dividere le informazioni in più classi, cambiare i nomi degli attributi, aggiungere nuovi attributi, ecc. Non puoi farlo così facilmente con un'entità come Dto.
  4. Non ottimizzato . La tua entità sarà sempre più grande di un semplice Dto. Quindi avrai sempre più informazioni da ignorare / serializzate e probabilmente più informazioni non necessarie da trasmettere.
  5. Problemi pigri . Utilizzando l'entità di alcuni framework (come Hibernate), se si tenta di recuperare alcune informazioni che non sono state caricate in modo pigro all'interno di una transazione del database, il proxy ORM non verrà collegato alla transazione e si riceverà una sorta di "eccezione pigra" solo per chiamare un getmetodo dell'entità.

Quindi, è più facile e sicuro utilizzare una sorta di strumento di mappatura per aiutarti in questo lavoro, mappando i campi dell'entità su un Dto.


1

Per completare ciò che ha detto @Dherik, i principali problemi dell'uso di oggetti entità come oggetti di trasferimento dati sono:

  1. In una transazione, si corre il rischio di impegnare le modifiche apportate alla propria entità perché la si utilizza come DTO (anche se è possibile staccare l'entità della sessione in una transazione, la maggior parte delle volte è necessario verificare questo stato prima qualsiasi modifica sulla tua entità-DTO e assicurati che non sei in una transazione o che la sessione è stata chiusa se non vuoi che le modifiche persistano).

  2. Dimensione dei dati condivisi tra il client e il server: a volte non si desidera inviare al client tutto il contenuto di un'entità per ridurre al minimo le dimensioni della risposta della richiesta. La separazione del DTO dall'entità è più flessibile, in modo da specializzare i dati che si desidera inviare in determinati casi d'uso.

  3. Visibilità e manutenzione: devi gestire le tue annotazioni jpa / ibernazione sui campi della tua entità e mantenere le annotazioni jackson per serializzare in json nello stesso posto (anche se puoi separarle dall'implementazione dell'entità inserendole nell'interfaccia ereditata dal entità). Quindi, se si modifica il contenuto DTO aggiungendo un nuovo campo, un'altra persona può probabilmente pensare che sia un campo dell'entità, quindi un campo della tabella interessato nel database (anche se è possibile utilizzare l' @Transientannotazione su tutti i campi DTO per il caso ..!).

Secondo me, crea rumore quando leggi l'entità, ma la mia opinione è sicuramente soggettiva.

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.