Approccio DDD alle operazioni CRUD di base in un'applicazione complessa incentrata sul dominio


9

La mia azienda sta riscrivendo la nostra applicazione Web da zero. È un'applicazione di livello aziendale di grandi dimensioni con un dominio complesso nel settore finanziario.

Stiamo usando un ORM (Entity framework) per la persistenza.

In sostanza, metà delle nostre applicazioni si concentra sulla raccolta di dati grezzi dall'utente, la loro memorizzazione, e poi l'altra metà dell'applicazione che contiene la maggior parte della nostra logica di dominio effettiva prende quei dati grezzi per creare la nostra immagine di dominio che differisce notevolmente da quella originale input non elaborati e li passa in un motore di calcolo, esegue calcoli e genera risultati, che vengono quindi visualizzati all'utente.

In un approccio DDD che utilizza i livelli, sembra che le operazioni CRUD attraversino il livello del dominio. ma almeno nel nostro caso, questo non sembra avere senso.

Quando un utente accede alla schermata di modifica per modificare un account di investimento, ad esempio, i campi sullo schermo sono i campi esatti memorizzati nel database, non la rappresentazione del dominio utilizzata in seguito per i calcoli. Quindi perché dovrei caricare la rappresentazione del dominio dell'account di investimento quando la schermata di modifica necessita della rappresentazione del database (input non elaborati)?

Dopo che l'utente ha fatto clic su "Fine" nella schermata dell'account di investimento e viene eseguito un POST al controller, il controller ora ha praticamente una rappresentazione esatta del database dell'account di investimento che deve salvare. Ma per qualche motivo, dovrei caricare la rappresentazione del dominio per apportare modifiche invece di mappare il modello del controller direttamente sul modello di database (modello del framework Entity)?

Quindi, in sostanza, sto mappando un modello di dati sul modello di dominio, solo così può essere mappato di nuovo sul modello di dati per persistere. Che senso ha?

Risposte:


9

Immagina di implementare la tua pagina di creazione dell'account mappando il post del modulo direttamente su un oggetto EF che viene quindi salvato nel database.

Supponiamo inoltre che il database abbia varie restrizioni che impediscono l'inserimento di dati completamente errati. Gli account hanno sempre clienti ecc.

Tutto sembra funzionare bene. Ma poi l'azienda stabilisce una nuova regola.

  • Gli account creati un giovedì ottengono un tasso di interesse del 2%. (supponiamo che il tasso di interesse sia uno dei campi del conto)

Ora devi mettere questa logica da qualche parte e non hai un oggetto dominio per inserirlo.

DDD presume che avrai sempre questo tipo di regole e probabilmente lo farai. La creazione di un account deve avere vari controlli, un registro di controllo ecc. Non sarà semplicemente "scrivere una riga nel db"

Pianifica il tuo dominio partendo dal presupposto che non ci siano persistenza o controller MVC con ulteriore logica in ingresso. Assicurati di acquisire tutti i requisiti e che siano tutti nel modello di dominio.


3
Questo è un buon modo per dirlo. Odio trovare regole commerciali mescolate a dettagli DB. +1
candied_orange

Aspetti positivi, ma cosa succede se queste regole di validazione si applicano solo durante la creazione e l'aggiornamento degli input dell'utente? Quindi, una volta che abbiamo gli input dell'utente, il modello creato durante l'esecuzione dei calcoli è un modello completamente diverso. Dovremmo avere due modelli di dominio per un conto di investimento? Uno per le operazioni CRUD di input non elaborati per l'utente e un altro per quando tali input vengono utilizzati per creare il modello di dominio utilizzato nei calcoli?
wired_in

confondere le mie domande. dovrai fare un esempio completo. Se hai una logica di dominio, dovrebbe andare in un oggetto dominio. Ciò non significa che non puoi creare un altro oggetto dominio in seguito dal primo
Ewan,

Immagina un motore di calcolo complesso. Uno degli input di cui ha bisogno per eseguire i calcoli è un conto di investimento, ma tutto il conto di investimento è per il motore di calcolo è un flusso di reddito per un certo periodo di tempo. Questo modello di dominio di un conto di investimento è completamente diverso dagli input non elaborati immessi dall'utente per questo conto di investimento. Tuttavia, quando l'utente immette input di base come Nome, Valore corrente, ecc., Deve comunque essere presente una logica di convalida, ma non deve avere nulla a che fare con il modello utilizzato dal motore di calcolo. Quindi ci sono due modelli di dominio per un conto di investimento qui?
wired_in

..... o forse avere un modello di account di investimento nel dominio è eccessivo per le operazioni CRUD e dovrebbero esserci solo alcuni attributi di
validazione

7

Che senso ha?

Risposta breve: non lo fa .

Risposta più lunga: i modelli più pesanti per lo sviluppo di un modello di dominio non si applicano a quelle parti della soluzione che sono solo un database.

Udi Dahan ha avuto un'interessante osservazione che può aiutare a chiarire questo

Dahan ritiene che un servizio debba avere sia una sorta di funzionalità che alcuni dati. Se non ha dati, allora è solo una funzione. Se tutto ciò che fa è eseguire operazioni CRUD sui dati, allora è un database.

Il punto del modello di dominio, dopo tutto, è quello di garantire che tutti gli aggiornamenti ai dati mantengano invariante il business corrente. Oppure, per dirla in altro modo, il modello di dominio è responsabile di garantire che il database che agisce come sistema di registrazione sia corretto.

Quando hai a che fare con un sistema CRUD, di solito non sei il sistema di registrazione per i dati. Il mondo reale è il libro dei record, e il tuo database è solo una rappresentazione memorizzata nella cache del mondo reale.

Ad esempio, la maggior parte delle informazioni che appaiono in un profilo utente, come un indirizzo e-mail o un numero di identificazione rilasciato dal governo, hanno una fonte di verità che vive al di fuori della tua attività - è l'amministratore di posta di qualcun altro che assegna e revoca gli indirizzi e-mail, non la tua app. È il governo che assegna SSN, non la tua app.

Quindi normalmente non eseguirai alcuna convalida del dominio sui dati che ti vengono dal mondo esterno; potresti avere controlli in atto per garantire che i dati siano ben formati e adeguatamente disinfettati ; ma non sono i tuoi dati - il tuo modello di dominio non ottiene il veto.

In un approccio DDD che utilizza i livelli, sembra che le operazioni CRUD attraversino il livello del dominio. ma almeno nel nostro caso, questo non sembra avere senso.

Questo è giusto per il caso in cui il database è il libro dei record .

Ouarzy ha detto così .

Lavorando su molti codici legacy, tuttavia, osservo errori comuni per identificare ciò che è all'interno del dominio e ciò che è all'esterno.

Un'applicazione può essere considerata CRUD solo se non esiste una logica aziendale attorno al modello di dati. Anche in questo (raro) caso, il tuo modello di dati non è il tuo modello di dominio. Significa solo che, poiché non è coinvolta alcuna logica aziendale, non abbiamo bisogno di alcuna astrazione per gestirla, e quindi non abbiamo un modello di dominio.

Utilizziamo il modello di dominio per gestire i dati che appartengono al dominio; i dati dall'esterno del dominio sono già gestiti da qualche altra parte - stiamo solo memorizzando nella cache una copia.

Greg Young utilizza i sistemi di magazzino come illustrazione principale di soluzioni in cui il libro dei record è altrove (ovvero: il piano di magazzino). L'implementazione che descrive è molto simile alla tua: un database logico per acquisire i messaggi ricevuti dal magazzino e quindi un database logico separato che memorizza nella cache le conclusioni tratte dall'analisi di tali messaggi.

Quindi forse abbiamo due contesti limitati qui? Ognuno con un modello diverso per uninvestment account

Può essere. Sarei riluttante ad etichettarlo come un contesto limitato, perché non è chiaro quale altro bagaglio lo accompagni. Potrebbe essere che tu abbia due contesti, potrebbe essere un contesto con sottili differenze nel linguaggio onnipresente che non hai ancora imparato.

Possibile cartina di tornasole: quanti esperti di dominio hai bisogno di due esperti di dominio per coprire questo spettro, o solo uno che parla dei componenti in modi diversi. Fondamentalmente, potresti essere in grado di indovinare quanti contesti limitati hai lavorando all'indietro la legge di Conway.

Se ritieni che i contesti limitati siano allineati ai servizi, potrebbe essere più semplice: dovresti essere in grado di distribuire queste due funzionalità in modo indipendente? Sì suggerisce due contesti limitati; ma se devono essere sincronizzati, forse è solo uno.


Bene, esiste una logica di convalida e di default, ma si applica solo durante la creazione / aggiornamento degli input non elaborati per un conto di investimento. Quindi utilizziamo un modello molto più ricco per l'account di investimento quando lo utilizziamo come input per un motore di calcolo. Quindi forse abbiamo due contesti limitati qui? Ognuno con un modello diverso per un conto di investimento "
wired_in

Sono appena tornato a questo dopo diversi anni e il tuo commento sta risuonando ora più di prima per qualche motivo. Ci sono un sacco di cose buone qui, ma potresti chiarire una cosa per me? Hai detto "Il punto del modello di dominio, dopo tutto, è garantire che tutti gli aggiornamenti ai dati mantengano invariante il business attuale". Ciò si applicherebbe alla parte della nostra app che salva / aggiorna le informazioni. L'altra parte è solo un motore di calcolo. Prende una rappresentazione dei dati come input e genera risultati. Allora non fa parte del modello di dominio? Dal momento che non influisce sui dati memorizzati?
wired_in

2

Nel tuo dominio non dovresti sapere che il database esiste.

Il tuo dominio riguarda le regole aziendali. Le cose che devono sopravvivere quando la società che ha creato il tuo database fallisce. Cioè, se vuoi che la tua azienda sopravviva. È davvero bello quando a quelle regole non importa che tu abbia cambiato il modo in cui conservi i dati.

I dettagli del database esistono e devono essere trattati. Dovrebbero vivere altrove. Mettili attraverso un confine. Controlla attentamente come comunichi attraverso quel confine o non è un confine.

Zio Bob ha questo da dire su cosa inserire i tuoi dati:

In genere i dati che attraversano i confini sono semplici strutture di dati. Se lo desideri, puoi utilizzare strutture di base o semplici oggetti Data Transfer. Oppure i dati possono essere semplicemente argomenti nelle chiamate di funzione. Oppure puoi comprimerlo in un hashmap o costruirlo in un oggetto.

L'importante è che strutture di dati isolate, semplici, passino attraverso i confini. Non vogliamo imbrogliare e passare le entità o le righe del database. Non vogliamo che le strutture di dati abbiano alcun tipo di dipendenza che viola la regola di dipendenza.

[…] Quando passiamo i dati attraverso un confine, è sempre nella forma più conveniente per il cerchio interno.

Architettura pulita

Spiega anche come i tuoi strati esterni debbano essere plug-in per i tuoi strati interni in modo che gli strati interni non sappiano nemmeno che esistono strati esterni.

Cheat sheet di architettura pulita

Segui qualcosa del genere e avrai un bel posto in cui ignorare il database in cui ti puoi preoccupare delle regole di convalida dell'input, regole che devono essere mantenute in qualche modo, regole per eseguire calcoli, regole per inviare tali risultati a qualsiasi output. In realtà è più facile leggere questo tipo di codice.

O quello o decidi che il tuo dominio è davvero solo per manipolare il database. In tal caso, la lingua del tuo dominio è SQL. Se è così bello, ma non aspettarti che l'implementazione delle regole aziendali sopravviva a un cambiamento nella persistenza. Finirai per doverli riscrivere completamente.


Stiamo utilizzando un ORM (Entity Framework), quindi il nostro database è già stato estratto, ma i modelli di dati (classi Entity framework) sono praticamente 1 a 1 con le tabelle del database. Il problema è che in alcune parti della nostra applicazione l'utente sta essenzialmente aggiornando il modello di dati (la schermata è solo un elenco di caselle di testo in cui ogni casella di testo è un campo nel database (modello di dati).
wired_in

Quindi non vedo un motivo per non usare solo le rappresentazioni dei dati grezzi (modello di dati) quando si eseguono operazioni CRUD. Abbiamo una rappresentazione di dominio complessa utilizzata per i calcoli, che è quello che considero il nostro modello di dominio, ma non vedo perché dovrei caricare quell'immagine nella parte CRUD della nostra applicazione.
wired_in

Definisci cosa intendi con "usa rappresentazioni dei dati grezzi". I dati vengono immessi, i dati vengono convalidati in base alle regole del dominio, i dati vengono persistiti in qualche modo, i dati vengono calcolati rispetto, i risultati vengono inviati a qualsiasi cosa. Mi sto perdendo qualcosa?
candied_orange,

Sto cercando di dire che i dati grezzi che otteniamo dall'utente per un conto di investimento non sono il modo in cui rappresentiamo tale conto di investimento nelle parti principali della nostra applicazione, come quando viene utilizzato per i calcoli. Ad esempio, potremmo avere un input booleano che salviamo nel database chiamato IsManagedAccount. L'utente ci fornisce questo tramite un pulsante di opzione nella schermata di modifica. Pertanto, la rappresentazione dal database allo schermo va da 1 a 1. Quando creiamo il nostro modello di dominio più avanti nell'applicazione, potremmo avere una classe ManagedAccount, quindi nessuna proprietà booleana. Le due strutture sono molto diverse.
wired_in

Quindi, quando l'utente sta semplicemente modificando gli input non elaborati su una schermata di modifica, perché dovrei caricare l'immagine del dominio e quindi aggiungere molta complessità per mappare in qualche modo la classe ManagedAccount fortemente tipizzata su una rappresentazione piatta che è solo una singola classe con un IsManagedAccount proprietà?
wired_in

1

Applicazione della teoria DDD:

Esistono due contesti limitati in quel dominio:

  • I calcoli del conto di investimento. Il modello matematico del conto di investimento è un elemento forse un aggregato.
  • Core Finance. Il conto di investimento del cliente è una delle entità.

Ogni contesto limitato può avere un design architettonico diverso.

Esempio:

Il conto di investimento del cliente è un'entità (forse un aggregato, dipende dal dominio) e la persistenza dei dati viene effettuata tramite il repository dell'entità (RDB o altro tipo di DB come un database OO).

Non esiste un approccio DDD alle operazioni CRUD. Avere un campo DB legato ai dati di un oggetto infrange i principi di progettazione.

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.