Valori calcolati e letture semplici: una seccatura per i miei disegni basati su dominio!


9

Il problema che continuo ad affrontare è come gestire i valori calcolati guidati dalla logica di dominio mentre si lavora ancora in modo efficiente con l'archivio dati.

Esempio:

Sto restituendo un elenco di prodotti dal mio repository tramite un servizio. Questo elenco è limitato dalle informazioni di impaginazione della richiesta DTO inviata dal client. Inoltre, il DTO specifica un parametro di ordinamento (un enum client-friendly).

In uno scenario semplice, tutto funziona alla grande: il servizio invia espressioni di paging e ordinamento al repository e il repository invia una query efficiente al DB.

Tutto ciò si interrompe, tuttavia, quando ho bisogno di ordinare i valori generati in memoria dal mio modello di dominio. Ad esempio, la classe Product ha un metodo IsExpired () che restituisce un bool basato sulla logica aziendale. Ora non riesco a ordinare e sfogliare a livello di repository: tutto sarebbe stato fatto in memoria (inefficiente) e il mio servizio avrebbe dovuto conoscere la complessità di quando inviare questi parametri al repository e quando eseguire ordinamento / paging si.

L'unico modello che sembra avere senso per me è quello di memorizzare lo stato dell'entità nel db (rendere IsExpired () un campo di sola lettura e aggiornarlo tramite la logica del dominio prima di salvare). Se separo questa logica in un repository "read model / dto" e "reporting" separato, sto rendendo il mio modello più anemico di quanto mi piacerebbe.

A proposito, ogni esempio che ho visto là fuori per calcoli come questo sembra davvero appoggiarsi all'elaborazione in memoria e al glossing sul fatto che è molto meno efficiente a lungo termine. Forse sto ottimizzando prematuramente, ma questo non mi sta proprio bene.

Mi piacerebbe sapere come gli altri hanno affrontato questo problema, dato che sono sicuro che sia comune su quasi un progetto che coinvolge DDD.

Risposte:


3

Non penso che avere due diversi modelli di dominio dello stesso modello di dati renda anemico il tuo dominio. Un modello di dominio anemico è quello in cui la logica aziendale che cambia spesso viene nascosta dal dominio in un livello di servizio (o, peggio, nel livello dell'interfaccia utente).

La separazione dei modelli di dominio dei comandi e delle query viene spesso adottata e ha un acronimo piacevole che puoi utilizzare in CQRS (Segregazione responsabilità responsabilità query comandi).

Impiegando il modello del modello di dominio, Udi Dahan

Mentre in passato ero "riuscito" nella creazione di un singolo modello a oggetti persistente che gestiva sia comandi che query, spesso era molto difficile ridimensionarlo, poiché ogni parte del sistema trascinava il modello in una direzione diversa.

Si scopre che gli sviluppatori spesso assumono requisiti più faticosi di quelli di cui l'azienda ha effettivamente bisogno. La decisione di utilizzare le entità del modello di dominio per mostrare informazioni all'utente è solo un esempio.

[...]

Per coloro che sono abbastanza grandi da ricordare, le migliori pratiche sull'uso di COM + ci hanno guidato a creare componenti separati per la logica di sola lettura e di lettura / scrittura. Eccoci, un decennio più tardi, con nuove tecnologie come Entity Framework, ma quegli stessi principi continuano a essere validi.

CQRS con attori Akka e modelli di dominio funzionali, Debasish Ghosh

Greg Young ha tenuto alcune fantastiche sessioni su DDD e CQRS. Nel 2008 ha dichiarato "Un singolo modello non può essere appropriato per la segnalazione, la ricerca e il comportamento transazionale". Abbiamo almeno due modelli: uno che elabora i comandi e invia le modifiche a un altro modello che serve query e report degli utenti. Il comportamento transazionale dell'applicazione viene eseguito attraverso il modello di dominio avanzato di aggregati e repository, mentre le query vengono fornite direttamente da un modello di dati non normalizzato.

CQRS, Martin Fowler

La modifica introdotta da CQRS consiste nel suddividere quel modello concettuale in modelli separati per l'aggiornamento e la visualizzazione, che fa riferimento rispettivamente a Comando e Query seguendo il vocabolario di CommandQuerySeparation. La logica è che per molti problemi, in particolare in settori più complicati, avere lo stesso modello concettuale per comandi e query porta a un modello più complesso che non funziona bene.

In breve, la tua idea di far gestire la scadenza al modello di comando e passarla al database va benissimo. Leggi il primo articolo sopra e vedrai scenari simili ma più complessi.


2

SPECIFICHE

So che hai già accettato una risposta, ma, hai chiesto informazioni su DDD, e la corrispondenza esatta per questo è ciò che Evans chiama una "specifica":
collegamento diretto a google books
Se quel collegamento non funziona, controlla il libro in questi risultati
È pagina 226 se hai il libro.

A pagina 227 sono 3 usi per le specifiche: Convalida, Selezione, Creazione di un nuovo oggetto speciale. La tua è "selezione" - IsExpired.

Un'altra cosa del concetto di "specificiton" è che ammette che, per motivi di efficienza, potrebbe essere necessaria una versione del codice per operare sugli oggetti in memoria e un'altra versione del codice per eseguire una query efficiente sul repository senza dover prima ottenere tutti gli oggetti nella memoria.

In un mondo semplice, ciò significherebbe mettere una versione SQL nel repository e una versione oggetti nel modello, ovviamente con degli svantaggi. La logica è in 2 posti (male, qualcuno dimenticherà di aggiornarsi su quei posti) e c'è una logica di dominio nel tuo repository.

Quindi la risposta è mettere entrambi i set di logica in una specifica. La versione in memoria, ovviamente, ma anche una versione del repository. Se si utilizza ad esempio n-ibernazione, è possibile utilizzare il linguaggio di query incorporato per la versione del repository.

Altrimenti dovrai creare un metodo di repository speciale per questa specifica che viene utilizzato dall'oggetto specifica. Le richieste di raccolte di oggetti corrispondenti alla specifica passerebbero attraverso la specifica, non il repository. E almeno il codice grida "Sono in 2 posti, non dimenticarlo" per i futuri manutentori. C'è un meraviglioso esempio a pagina 231-232 per risolvere un problema molto simile.

La specifica è una perdita / slittamento "consentito" della "purezza" di DDD. Potrebbe non soddisfare le tue esigenze per vari scopi. Ad esempio, l'ORM potrebbe generare un SQL errato; potrebbe esserci troppa codifica extra. Quindi potresti dover chiamare metodi di repository in modo tale che è quasi come mettere SQL nella specifica. Una brutta cosa ovviamente. Ma non dimenticare, il tuo programma deve funzionare a una velocità ragionevole. Non deve vincere un premio di purezza DDD. Quindi una realtà di commutazione dei datastore potrebbe significare un impennamento vecchio stile nel programma. Anche una cosa negativa. Ma non è così male come un programma lento (aka SUCKing). Se esaurire la confezione su vari DB è una realtà, ovviamente, duplicherai le regole di business per ciascun archivio dati per ogni specifica. Almeno hai il dito sull'argomento e puoi usare il modello di strategia quando scambi i repository. Ma se stai già utilizzando un DB specifico, ricordaYAGNI.

Per quanto riguarda CQRS: la citazione di Fowler di pdr sopra è ancora valida qui: "avere lo stesso modello concettuale per comandi e query porta a un modello più complesso che non funziona bene" ... e potrebbe essere necessario utilizzare CQRS o simili. Ma è molto più costoso dal punto di vista dello sviluppo e della manutenzione. Se sei un fornitore di pacchetti in concorrenza con altri, potrebbe pagare. Se stai scrivendo un'app LOB personalizzata per un cliente, scattare per la perfezione è una scelta sbagliata. Devi decidere se il valore di avere un modello completamente o principalmente doppio vale lo sforzo extra. specificazioneè un buon compromesso perché ti permette di fare questa separazione in una piccola parte del programma che ne ha bisogno, con la velocità (sviluppo) e la semplicità di un modello. In bocca al lupo!


Questo ha perfettamente senso. Penso di dover mordere il proiettile e leggere il libro di Evans :-) Sto vedendo ora che avere una comprensione superficiale di questi concetti può davvero paralizzarti!
drogon

0

Immagino che vorrei mettere in discussione quale sia la logica aziendale che determina se IsExpired è vero o no. Questa logica può essere eseguita da una query allo stato attuale del modello dati? In tal caso, puoi rendere il tuo repository abbastanza intelligente da utilizzare la logica "isExpired" quando chiedi prodotti in un certo modo? In caso contrario, forse è necessario riesaminare il modello di dati.

DDD non significa che i tuoi repository debbano essere stupidi - significa solo che il tuo dominio deve sapere come parlare ai tuoi repository.

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.