Condividi tramite


Concetti sui dati

Programmazione per DDD (Domain-Driven Design): suggerimenti per sviluppatori attenti ai dati

Julie Lerman

Scarica il codice di esempio

Julie LermanQuest'anno, Eric Evans software innovativo design book, "Domain-Driven Design: Affrontare la complessità nel cuore del Software"(Addison-Wesley Professional, 2003, amzn.to/ffL1k), celebra il suo decimo anniversario. Evans ha portato a questo libro molti anni di esperienza di guida grandi imprese attraverso il processo di creazione di software. Poi ha trascorso più anni pensando a come incapsulare i motivi che portano questi progetti di successo — interagendo con il cliente, analizzare i problemi di affari essendo risolti, costruzione di squadre e architettura del software. Il focus di questi modelli è dominio di business, e insieme essi comprendono Domain-Driven Design (DDD). Con DDD, è modello del dominio in questione. Il risultato di modelli da questa astrazione delle vostre conoscenze sul dominio. Rileggendo la prefazione di Martin Fowler e prefazione 'Evans, ancora oggi, continua a fornire una ricca panoramica dell'essenza di DDD.

In questa colonna e i successivi due pure, potrai condividere alcune indicazioni che hanno aiutato il mio cervello Entity Framework incentrati sui dati, ottenere chiarezza come lavoro su come ottenere il mio codice di beneficiare di alcuni modelli tecnici DDD.

Perché si preoccupano DDD?

La mia introduzione a DDD è venuto da una breve video-intervista InfoQ.com con Jimmy Nilsson, un architetto rispettato nella comunità .NET (e altrove), che stava parlando di LINQ to SQL e il Entity Framework (bit.ly/11DdZue).Alla fine, Nilsson è chiesto di nominare il suo libro preferito tech.Sua risposta: "Il mio libro preferito computer è il libro di Eric Evans,"Domain-Driven Design." È come la poesia, credo.Non è solo grande contenuto, ma è possibile leggere molte volte e si legge come la poesia." Poesia!Stavo scrivendo il mio primo libro di tech, "programmazione Entity Framework" (o ' Reilly Media, 2009), al momento, e sono stato incuriosito da questa descrizione.Così sono andato e ho letto un po ' del libro 'Evans, per vedere com'era.Evans è un scrittore di bello, fluido.E questo, combinato con il suo punto di vista percettivo, naturalistico di sviluppo software, rendere il libro una gioia di leggere.Ma sono anche rimasto sorpreso da ciò che stavo leggendo.Non solo era la scrittura meraviglioso, che cosa stava scrivendo circa mi ha incuriosito.Ha parlato della costruzione di rapporti con i clienti e capire veramente le loro imprese e i loro problemi di business (relativi al software in questione), a percussione non solo codice.Questo è qualcosa che è stato importante per me nei miei 25 anni di sviluppo software.Volevo più.

I punta di piedi attorno al bordo di DDD per un paio di anni, poi ha iniziato a imparare di più — incontro Evans in una conferenza e poi frequentando il suo workshop immersion di quattro giorni.Mentre io sono lontano da un esperto in DDD, ho trovato che il modello di contesto delimitato era qualcosa che potrei sfruttare subito come ho lavorato per spostare il mio processo di creazione del software verso una struttura più organizzata e gestibile.Si può leggere su questo argomento nel mio articolo di gennaio 2013, "Shrink EF modelli con DDD contesti delimitata" (msdn.microsoft.com/magazine/jj883952).

Da allora io ho esplorato ulteriormente.Io sono incuriosito e ispirato da DDD, ma lotta con la mia prospettiva basata sui dati di comprendere alcuni dei modelli tecnici che fanno successo.Sembra probabile che molti sviluppatori passare attraverso la stessa lotta, così ho intenzione di condividere alcune delle lezioni che ho imparato con l'aiuto, interesse e generosità di Evans e un certo numero di altri DDD professionisti e insegnanti, tra cui Paul Rayner, Vaughn Vernon, Greg Young, Cesar de la Torre e Yves Reynhout.

Quando il dominio di modellazione, dimenticare la persistenza

Modellazione del dominio è tutto incentrato sulle attività di business.Quando progettare tipi e loro proprietà e comportamenti, sono fortemente tentato di pensare a come un rapporto funzionerà nel database e come framework object relational mapping (ORM) di scelta —Entity Framework— tratterà le proprietà, relazioni e gerarchie di ereditarietà che sto costruendo.A meno che non si sta costruendo software per un'azienda cui business è data storage and retrieval — qualcosa come Dropbox, persistenza dei dati svolge solo un ruolo di supporto nell'applicazione.È molto simile facendo una chiamata fuori all'API di una fonte tempo al fine di visualizzare la temperatura attuale a un utente.O l'invio di dati dall'applicazione a un servizio esterno, forse una registrazione su Meetup.com.Naturalmente, i dati possono essere più complicati, ma con un approccio DDD per contesti di delimitazione, concentrandosi su comportamenti e orientamenti DDD quando tipi di edifici, la persistenza può essere molto meno complessa rispetto ai sistemi che possono costruire oggi.

E se tu hai studiato fino sul tuo ORM, come imparare a configurare i mapping di database utilizzando il Entity Framework Fluent API, si dovrebbe essere in grado di rendere la persistenza lavorare come necessario.Nel peggiore dei casi, potrebbe essere necessario apportare alcune modifiche per le classi.In casi estremi, come con un database legacy, si potrebbe anche aggiungere in un modello di persistenza progettato per il mapping del database, quindi utilizzare qualcosa come AutoMapper per risolvere le cose tra il modello di dominio e il tuo modello di persistenza.

Ma queste preoccupazioni sono estranei al problema business che software è volto a risolvere, quindi persistenza non dovrebbe interferire con il design di dominio.Questa è una sfida per me, perché come io sto progettando la mia entità, non posso fare a meno di considerare come EF dedurrà i mapping di database.E così cerco di bloccare quel rumore.

Metodi Public e private Setter

Un'altra regola è di rendere setter di proprietà privata.Invece di consentire la chiamata di codice per impostare in modo casuale le varie proprietà, si dovrebbe controllare l'interazione con gli oggetti DDD e loro dati correlati utilizzando metodi che modificano le proprietà.E, no, non intendo metodi come SetFirstName e SetLastName.Per esempio, invece di un'istanza di un nuovo tipo di cliente e quindi impostando ogni sua proprietà, si potrebbero avere alcune regole da considerare quando si crea un nuovo cliente.Puoi costruire quelle regole nel costruttore del cliente, utilizzare un metodo Factory Pattern o anche avere un metodo Create nel tipo di cliente.Figura 1 Mostra un tipo di cliente che è definito seguendo il modello DDD di un'aggregazione radice (ovvero, "padre" di un grafico di oggetti, indicato anche come una "entità radice" in DDD).Proprietà Customer hanno privato setter che solo altri membri della classe Customer possono direttamente influenzare tali proprietà.La classe espone un costruttore per controllare come viene creata un'istanza e nasconde il costruttore senza parametri (richiesto da Entity Framework) come interna.

Figura 1 proprietà e i metodi di un tipo che agisce come un aggregato Root

public class Customer : Contact
{
  public Customer(string firstName,string lastName, string email)
  { ... }
  internal Customer(){ ... }
  public void CopyBillingAddressToShippingAddress(){ ... }    
  public void CreateNewShippingAddress(
   string street, string city, string zip) { ... }
  public void CreateBillingInformation(
   string street, string city, string zip,
   string creditcardNumber, string bankName){ ... }    
  public void SetCustomerContactDetails(
   string email, string phone, string companyName){ ... }
  public string SalesPersonId { get; private set; }
  public CustomerStatus Status{get;private set;}
  public Address ShippingAddress { get; private set; }
  public Address BillingAddress { get;private set; }
  public CustomerCreditCard CreditCard { get; private set; }
}

Il tipo di cliente controlla e protegge le altre entità nell'aggregato — alcuni indirizzi e un tipo di carta di credito, esponendo metodi specifici (ad esempio CopyBillingAddressToShippingAddress) con cui questi oggetti saranno creati e manipolati. La radice di aggregata dovrà assicurarsi che vengono applicate le regole che definiscono ogni entità all'interno dell'aggregato utilizzando la logica di dominio e il comportamento implementato in questi metodi. Più importante, la radice aggregata è responsabile della logica invariante e coerenza in tutto l'aggregato. ' Ll talk more about invarianti nel mio prossimo articolo, ma per l'intanto, si consiglia di leggere il post del blog di Jimmy Bogard, "rafforzare il tuo dominio: Aggregazione edilizia,"a bit.ly/ewNZ52, che fornisce un'ottima spiegazione di invarianti in aggregati.

Alla fine, ciò che è esposto dal cliente è comportamento anziché le proprietà: CopyBillingAddressToShippingAddress, CreateNewShipping­indirizzo, CreateBillingInformation e SetCustomerContactDetails.

Nota che il contatto digitare, da quale cliente deriva, vive in un assembly diverso denominato "Comune" perché può essere necessario da altre classi. Ho bisogno di nascondere le proprietà del contatto, ma non possono essere privati o cliente non sarebbe in grado di accedervi. Invece, stai con ambiti come Protected:

public class Contact: Identity
{
  public string CompanyName { get; protected set; }
  public string EmailAddress { get; protected set; }
  public string Phone { get; protected set; }
}

Nota a margine sull'identità: Cliente e contatto può apparire come oggetti di valore DDD perché non hanno nessun valore chiave. Tuttavia, nella mia soluzione, il valore della chiave è fornito dalla classe identità da cui deriva il contatto. E nessuno di questi tipi non sono modificabili, quindi non possono essere considerati gli oggetti di valore, comunque.

Perché cliente eredita da contatto, avrà accesso a tali proprietà protette ed è in grado di impostare, come in questo metodo di SetCustomerContactDetails:

public void SetCustomerContactDetails (string email, telefono di stringa, stringa companyName)

{

EmailAddress = e-mail;

Telefono = telefono;

CompanyName = companyName;

}

A volte è necessario CRUD

Non tutto nella tua app deve essere creato utilizzando DDD. DDD è lì per aiutare a gestire i comportamenti complessi. Se devi solo fare qualche editing raw, casuale o query, quindi una semplice classe (o insieme di classi), definito proprio come si farebbe normalmente con EF primo codice (utilizzando proprietà e relazioni) e combinato con insert, update e delete metodi (tramite un repository o appena DbContext), è tutto il che necessario. Così, per realizzare qualcosa come la creazione di un ordine e i relativi elementi line, potreste volere DDD per aiutare a lavorare attraverso comportamenti e regole aziendali speciali. Ad esempio, questo è un cliente di stella d'oro dell'ordine? In tal caso, è necessario ottenere qualche cliente dettagli per determinare se la risposta è sì e, in caso affermativo, si applica uno sconto del 10 per cento per ogni elemento aggiunto all'ordine. Ha l'utente ha fornito loro informazioni sulla carta di credito? Quindi potrebbe essere necessario chiamare per un servizio di verifica per assicurarsi che sia una carta valida.

La chiave in DDD è di includere la logica di dominio come metodi all'interno di classi di entità di dominio, approfittando della OOP invece di implementare "script transazionale" all'interno di oggetti business apolide, che è un tipico demo-ware codice che assomiglia a First class.

Ma a volte tutto quello che stai facendo è qualcosa di molto standard, come la creazione di un record di contatto: nome, indirizzo, cui fa riferimento e così via e salvarlo.  Che è appena create, read, update e delete (CRUD). Non è necessario creare aggregazioni e radici e comportamenti che soddisfare.

Molto probabilmente l'applicazione conterrà una combinazione di comportamenti complessi e semplici CRUD. Prendete il tempo di chiarire i comportamenti e non perdere tempo, energia e denaro over-architecting i pezzi della tua app che sono davvero semplici. In questi casi, è importante identificare i confini tra i diversi sottosistemi o contesti delimitati. Un contesto limitato potrebbe essere molto basato sui dati (solo CRUD), mentre un contesto critico core-dominio-delimitata deve, d'altra parte, essere progettato seguendo approcci DDD.

Dati condivisi possono essere una maledizione in sistemi complessi

Un altro problema sbattuto la testa su, poi sbraitava e piagnucolò circa come persone gentilmente ha cercato di spiegare ulteriormente, riguarda la condivisione di dati e tipi attraverso sottosistemi. È diventato chiaro che io non riuscivo a "avere la mia torta e mangiare troppo," così sono stato costretto a tornare a pensare la mia ipotesi che assolutamente positivamente Devo condividere tipi attraverso sistemi e avere quei tipi che tutti interagire con la stessa tabella dello stesso database.

Sto imparando a considerare davvero dove ho bisogno di condividere i dati e poi prendere le mie battaglie. Alcune cose proprio non possono essere la pena di provare, come mappatura da differenti contesti per una singola tabella o anche un singolo database. L'esempio più comune è un contatto che sta cercando di soddisfare le esigenze di tutti attraverso sistemi di condivisione. Come conciliare e sfruttare il controllo del codice sorgente per un tipo di contatto che possa essere necessari in numerosi sistemi? Cosa succede se un sistema ha bisogno di modificare la definizione di tale tipo di contatto? Per quanto riguarda un ORM, come mappare un contatto che è utilizzato in sistemi a una sola tabella in un database unico?

DDD guida dalla condivisione di modelli di dominio e dati spiegando che non sempre è necessario scegliere la stessa tabella di persona in un unico database.

Il mio più grande spingere indietro con questo si basa su 25 anni di messa a fuoco sui benefici del riutilizzo — riutilizzo del codice e il riutilizzo dei dati. Così, ho un momento difficile con l'idea, ma sto riscaldando ad esso: Non è un crimine per duplicare i dati. Non tutti i dati si inseriranno in questo paradigma nuovo (per me), naturalmente. Ma che cosa circa qualcosa di leggero come nome di una persona? Così che cosa se si duplica una persona prima e cognome in più tabelle o persino più database che sono dedicati a diversi sottosistemi della soluzione software? Nel lungo periodo, lasciando andare la complessità della condivisione dei dati, è rendere il lavoro di costruzione di sistema molto più semplice. In ogni caso, deve sempre minimizzare la duplicazione di dati e attributi in diversi contesti delimitati. A volte basta solo ID e lo status del cliente al fine di calcolare sconti in un contesto di Pricing-delimitata. Che il cliente stesso il primo e ultimo nome potrebbe essere necessaria solo nel contesto di gestione contatti-delimitata.

Ma c'è ancora tanto informazioni che devono essere condivise tra i sistemi. È possibile sfruttare ciò che DDD si riferisce a come un "anti -­livello di corruzione" (che può essere qualcosa di semplice come un servizio o una coda di messaggi) per garantire che, per esempio, se qualcuno crea un nuovo contatto in un unico sistema, si sia riconoscere che la persona esiste già altrove, o assicurarsi che la persona, insieme con una chiave di identità comune, viene creata in un altro sottosistema.

Un sacco di masticare su fino al mese prossimo

Come fare il mio modo attraverso imparare e comprendere il lato tecnico di Domain-Driven Design, lottando per conciliare le vecchie abitudini con nuove idee e arrivando a innumerevoli "aha!" momenti, i puntatori che ho discusso qui sono veramente quelli che mi ha aiutato a vedere più luce che tenebre. A volte è solo una questione di prospettiva, e il modo che ho qui espresse li riflette la prospettiva che hanno contribuito a rendere le cose più chiare per me.

Io condividere alcuni di più del mio "aha!" momenti nel mio prossimo articolo, dove potrai parlare di quel termine condiscendente potreste aver sentito: "modello di dominio anemico," insieme a suo cugino DDD, il "modello di dominio ricco." Mi occuperò anche relazioni unidirezionali e cosa aspettarsi quando è il momento di aggiungere nella persistenza dei dati se si sta utilizzando il Entity Framework. Verrà anche tocco su alcuni argomenti DDD più che mi ha causato un sacco di dolore nel tentativo di ridurre la propria curva di apprendimento.

Fino ad allora, perché non dare un'occhiata più da vicino le proprie classi e vedere come essere più di un maniaco del controllo, nascondendo quei setter di proprietà e di esporre metodi più espliciti e descrittivi. E ricordate: non "SetLastName" i metodi consentiti. Che è barare!

Julie Lerman è un Microsoft MVP, mentore e consulente .NET che risiede nel Vermont. È possibile trovare le sue presentazioni relative all'accesso ai dati e altri argomenti su Microsoft .NET in occasioni di conferenze che si tengono in tutto il mondo. Blog di lei a /Blog ed è l'autore di "programmazione Entity Framework" (2010) così come un codice prima edizione (2011) e un DbContext (2012), tutti da o ' Reilly Media. Seguirla su Twitter a Twitter.com /julielerman. e vedere i suoi corsi di Pluralsight a Juliel.me/PS-video

Grazie all'esperto tecnica seguente per la revisione di questo articolo: Cesar de la Torre (Microsoft)