Best practice per la serializzazione di aggregati DDD


23

Secondo il dominio DDD, la logica del dominio non deve essere inquinata da problemi tecnici come la serializzazione, la mappatura relazionale degli oggetti, ecc.

Quindi, come si fa a serializzare o mappare lo stato degli aggregati senza esporlo pubblicamente tramite getter e setter? Ho visto molti esempi per esempio implementazioni di repository, ma praticamente tutti si basavano su accessi pubblici sulle entità e oggetti di valore per la mappatura.

Potremmo usare la riflessione per evitare accessi pubblici, ma IMO questi oggetti di dominio dipenderebbero ancora implicitamente dalla preoccupazione di serializzazione. Ad esempio, non è possibile rinominare o rimuovere un campo privato senza modificare la configurazione di serializzazione / mappatura. Quindi devi considerare la serializzazione dove dovresti invece concentrarti sulla logica del dominio.

Quindi qual è il miglior compromesso da seguire qui? Vivi con gli accessi pubblici, ma evita di usarli per nient'altro che mappare il codice? O mi sono perso qualcosa di ovvio?

Sono esplicitamente interessato a serializzare lo stato degli oggetti di dominio DDD (aggregati costituiti da entità e oggetti valore). Non si tratta della serializzazione in scenari di script generali o di transcation in cui i servizi senza stato operano su semplici oggetti contenitore di dati.

Risposte:


12

Tipi di oggetti

Ai fini della nostra discussione, separiamo i nostri oggetti in tre diversi tipi:

Logica del dominio aziendale

Questi sono gli oggetti che portano a termine il lavoro. Spostano denaro da un conto corrente a un altro, eseguono gli ordini e tutte le altre azioni che prevediamo di intraprendere il software aziendale.

Gli oggetti logici di dominio normalmente non richiedono accessori (getter e setter). Piuttosto, si crea l'oggetto passandogli le dipendenze attraverso un costruttore e quindi manipolando l'oggetto attraverso metodi (dire, non chiedere).

Oggetti di trasferimento dati

Gli oggetti di trasferimento dati sono allo stato puro; non contengono alcuna logica aziendale. Avranno sempre accessori. Possono avere o meno setter, a seconda che tu li scriva o meno in modo immutabile . O imposterai i tuoi campi nel costruttore e i loro valori non cambieranno per la durata dell'oggetto, oppure i tuoi accessori saranno letti / scritti. In pratica, questi oggetti sono in genere mutabili, in modo che un utente possa modificarli.

Visualizza oggetti modello

Visualizza oggetti modello contengono una rappresentazione dei dati visualizzabile / modificabile. Possono contenere una logica aziendale, generalmente limitata alla convalida dei dati. Un esempio di un oggetto Visualizza modello potrebbe essere InvoiceViewModel, contenente un oggetto Cliente, un oggetto Intestazione fattura ed Elementi pubblicitari fattura. Visualizza gli oggetti del modello contengono sempre accessori.

Quindi l'unico tipo di oggetto che sarà "puro", nel senso che non contiene gli accessi ai campi, sarà l'oggetto Logica di dominio. La serializzazione di un tale oggetto salva il suo attuale "stato computazionale", in modo che possa essere recuperato in seguito per completare l'elaborazione. Visualizza modelli e DTO possono essere liberamente serializzati, ma in pratica i loro dati vengono normalmente salvati in un database.

Serializzazione, dipendenze e accoppiamento

Mentre è vero che la serializzazione crea dipendenze, nel senso che devi deserializzare un oggetto compatibile, non ne consegue necessariamente che devi cambiare la configurazione della serializzazione. I buoni meccanismi di serializzazione sono di uso generale; a loro non importa se cambi il nome di una proprietà o di un membro, purché possa ancora mappare i valori ai membri. In pratica, ciò significa solo che è necessario ricerializzare l'istanza dell'oggetto per rendere la rappresentazione di serializzazione (xml, json, qualunque cosa) compatibile con il nuovo oggetto; non dovrebbero essere necessarie modifiche alla configurazione del serializzatore.

È vero che gli oggetti non dovrebbero preoccuparsi di come sono serializzati. Hai già descritto un modo per separare tali preoccupazioni dalle classi di dominio: la riflessione. Ma il serializzatore dovrebbe preoccuparsi di come serializza e deserializza gli oggetti; quella, dopo tutto, è la sua funzione. Il modo in cui tieni gli oggetti disaccoppiati dal processo di serializzazione è di rendere la serializzazione una funzione di uso generale , in grado di funzionare con tutti i tipi di oggetti.

Una delle cose di cui le persone si confondono è che il disaccoppiamento deve avvenire in entrambe le direzioni. Non è così; deve funzionare solo in una direzione. In pratica, non puoi mai disaccoppiare completamente; c'è sempre qualche accoppiamento. L'obiettivo dell'accoppiamento libero è facilitare la manutenzione del codice, non rimuovere tutte le dipendenze.


Sono d'accordo con la tua opinione sul disaccoppiamento. Il serializzatore dipende dall'oggetto dominio e va bene. Ma non viceversa. Non sono d'accordo tuttavia con la tua opinione sugli accessi pubblici sugli oggetti di dominio. In pratica spesso li hanno, sì. Ma IMO sarebbe preferibile implementare la logica di dominio in un design pulito orientato agli oggetti: dillo, non chiedere . Ma hai ancora bisogno di accessori per la mappatura (ORM, serializzazione, GUI ...). Ed è quello che vorrei evitare, se possibile.
EagleBeak,

Come pensi di accedere ai tuoi campi, se non disponi di accessori?
Robert Harvey,

In realtà non mi riferisco a nessuno dei tre tipi di oggetti che descrivi, ma agli "aggregati" nella terminologia DDD e ai loro oggetti secondari (entità, oggetti valore). Ora mi rendo conto che la mia domanda non era abbastanza esplicita al riguardo. Scusate! Si prega di vedere la mia modifica sopra.
EagleBeak,

1
Questo è sostanzialmente un problema irrisolto: non puoi avere incapsulamento, disaccoppiamento e serializzazione \ codifica allo stesso tempo esponendo DTO, è un modo per raggiungere un compromesso. Vi sono, tuttavia, molto meno intrusivo modi: yegor256.com/2016/07/06/data-transfer-object.html
Basilevs

1
Questo lascia cadere l'incapsulamento, chiunque può implementare o usare la classe friend per leggere gli interni dell'oggetto.
Basilevs

-1

Lo scopo di base della serializzazione è garantire che i dati prodotti da un sistema possano essere consumati da uno o più sistemi compatibili.

L'approccio più semplice e più solido alla serializzazione consiste nel tradurre i dati in un formato agnostico di tipo che mantenga la struttura in un formato semplice e di facile utilizzo. Ad esempio, i formati di serializzazione più diffusi (ad es. JSON, XML) utilizzano un formato di testo ben definito. Il testo è semplice da produrre, trasmettere e consumare.

Ci sono 2 motivi per cui l'utilizzo di uno di questi formati potrebbe non essere l'ideale.

  1. Efficienza

    C'è un costo inerente alla traduzione di tutti i dati nei loro equivalenti testuali. I tipi di dati non esisterebbero se il testo fosse il modo più efficiente per esprimere tutte le diverse forme di dati. Inoltre, la struttura di questi formati non è ideale per il recupero di sottoinsiemi di dati in modo asincrono o in parti.

    Ad esempio, XML e JSON presumono che i dati utilizzati verranno scritti e letti dall'inizio alla fine. Per l'elaborazione di set di dati molto grandi in cui la memoria è scarsa, il sistema che consuma i dati potrebbe richiedere la possibilità di elaborare i dati in parti. In tal caso, potrebbe essere necessaria un'implementazione di serializzazione / deserializzazione per scopi speciali per gestire i dati.

  2. Precisione

    Il casting richiesto per serializzare / deserializzare i dati dal tipo previsto al tipo agnostico di dati comporta una perdita di precisione.

Si potrebbe sostenere che produrre una rappresentazione binaria di oggetti e dati sia chiaramente la soluzione più efficiente e accurata. Il principale svantaggio è che l'implementazione di tutti i sistemi che consumano e producono dati devono rimanere compatibili. È un semplice vincolo in teoria, ma è un incubo da mantenere in pratica poiché i sistemi di produzione tendono a cambiare / evolversi nel tempo.

Detto questo. Il disaccoppiamento della serializzazione / deserializzazione dai dettagli specifici del dominio ha senso come regola generale perché i formati per scopi generali sono più robusti, supportati meglio su sistemi diversi e richiedono un sovraccarico di manutenzione pressoché nullo.


Siamo spiacenti, ma questo non risponde alla mia domanda. Si tratta di disaccoppiare gli oggetti di dominio dalla serializzazione, non i motivi della serializzazione o dei pro e contro di vari formati. Come posso serializzare oggetti di dominio senza esporre pubblicamente il loro stato privato?
EagleBeak,

@EagleBeak Oh, non mi rendevo conto che la tua preoccupazione riguardava specificamente la gestione dei membri privati. Nel tuo caso potresti serializzare in binario (supponendo che il sistema di ricezione segua le stesse regole / strutture in cui sono stati creati gli oggetti di dominio) o scrivere una logica che estrae solo i dati pubblici prima della serializzazione.
Evan Plaice,

Penso che il "solito" presupposto sia che i dati che vengono serializzati in un formato generico (ex xml, json) siano pubblici e che il privilegio sia controllato tramite l'API tramite ACL o altri equivalenti. La serializzazione / deserializzazione per scopi generici ricade maggiormente sulla scia del disaccoppiamento dei dati dalla logica aziendale passando da un sistema all'altro.
Evan Plaice,

Concordo sul fatto che gli accessi pubblici vengano generalmente assunti su oggetti da serializzare. Ma vorrei ancora sapere di più su come ciò si collega al DDD e alla sua forte attenzione all'incapsulamento della logica del dominio. Tutti i professionisti DDD espongono semplicemente lo stato del modello di dominio tramite gli accessi pubblici per la serializzazione (e non lo menzionano mai nei loro esempi)? Ne dubito. Per favore, non fraintendetemi. Apprezzo molto il tuo contributo. È solo che mi interessa un aspetto diverso. (Finora non penso che la mia domanda sia troppo vaga, ma la risposta di Robert Harvey e la tua mi ha fatto pensare a quello ..)
EagleBeak,
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.