2010-02-28 13 views
29

Le entità di dominio devono essere esposte come interfacce o come oggetti semplici?Le entità di dominio devono essere esposte come interfacce o come oggetti semplici?

L'interfaccia utente:

public interface IUser 
{ 
    string FirstName { get; set; } 
    string LastName { get; set; } 
    string Email { get; set; } 
    Role Role { get; set; } 
} 

L'attuazione utente (Implementato in layer LinqToSql Data Access):

public class User : IUser 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string Email { get; set; } 
    public Role Role { get; set; } 
} 

L'attuazione utente (implementato nel livello di NHibernate Data Access):

[NHibernate.Mapping.Attributes.Class] 
public class User : IUser 
{ 
    [NHibernate.Mapping.Attributes.Property] 
    public string FirstName { get; set; } 

    [NHibernate.Mapping.Attributes.Property] 
    public string LastName { get; set; } 

    [NHibernate.Mapping.Attributes.Property] 
    public string Email { get; set; } 

    [NHibernate.Mapping.Attributes.Property] 
    public Role Role { get; set; } 
} 

questo illustra solo alcune implementazioni DAL specifiche, non ha un campione migliore in questo momento.

risposta

25

La mia sensazione su questo è che gli oggetti dominio (non dominio entità, poiché tale titolo implica qualcosa a che fare con un database) non devono essere interfacce a meno che non si abbia una ragione molto convincente per credere che sarà necessario supportare più implementazioni ad un certo punto nel futuro.

Si consideri che il modello di dominio è il modello umano. Il business/servizio/documento è, letteralmente, il dominio. Molti di noi stanno sviluppando software per una singola azienda o scopo. Se il modello di dominio cambia, è perché le regole di business sono cambiate, e quindi il vecchio modello di dominio non è più valido - non c'è motivo di mantenere quello vecchio in giro, correndo accanto a quello nuovo.

Il dibattito non è ovviamente in bianco e nero. Potresti sviluppare software pesantemente personalizzato su più siti client. Potrebbe essere necessario implementare contemporaneamente diversi set di regole aziendali e contemporaneamente avere una reale necessità di adattarle a un'architettura unificata. Ma, almeno nella mia esperienza, questi casi sono l'eccezione piuttosto che la regola, e anche se generalmente non mi piace molto il termine, questo potrebbe essere il caso in cui dovresti pensare a te stesso, YAGNI.

L'accesso ai dati è un'area comune in cui si desiderano astrazioni migliori (persistence ignorance). Nel tuo esempio, hai gli attributi di NHibernate sulla tua classe del modello. Ma aggiungere gli attributi di persistenza non rende più una vera classe di dominio perché introduce una dipendenza su NHibernate. NHibernate e Fluent NHibernate supportano la mappatura dei POCO utilizzando una dichiarazione di mappatura esterna invece degli attributi sulla classe di dati e questo tende ad essere l'approccio preferito quando si utilizzano ORM come NHibernate o EF4, perché interrompe la dipendenza tra modello di persistenza e modello di dominio.

Se questi metodi di mappatura non sono stati supportati, e si avuto utilizzare gli attributi, allora potrei davvero suggerire utilizzando interfacce, invece, ma ORM oggi sono più sofisticati di quello, con la riflessione e proxy dinamici e il metodo di intercettazione a che fare la maggior parte del sollevamento pesante, quindi non è necessario creare le proprie astrazioni qui.

Alcuni tipi di oggetti che si desidera desidera esporre come interfacce sono:

  • Repository, che sono responsabili per il caricamento/salvataggio oggetti di dominio;
  • Plugin/estensioni del programma;
  • Modelli di View/Presenter, in modo che possano essere inserite diverse UI;
  • Tipi di dati astratti con molte implementazioni (matrice, elenco, dizionario, set di record e tabella dati sono tutte sequenze AKA IEnumerable);
  • Operazioni astratte con molti algoritmi possibili (ordinamento, ricerca, confronto);
  • Modelli di comunicazione (stesse operazioni su TCP/IP, named pipe, RS-232);
  • Qualsiasi specifica della piattaforma, se si prevede di eseguire l'implementazione su più di una (Mac/Windows/* nix).

Questo è in alcun modo un elenco completo, ma dovrebbe illuminare il principio di base qui, cioè che le cose più adatto di interfacciarsi astrazioni sono le cose che:

  1. dipendono da fattori che possono al di là del tuo controllo;
  2. È probabile che cambi in futuro; e
  3. Sono funzioni orizzontali (utilizzate in molte parti dell'app/architettura).

Una classe di dominio sarà ampiamente utilizzata, ma non si adatta a nessuna delle prime due categorie; non è probabile che cambi, e hai quasi il controllo completo sul design. Pertanto, a meno che le classi stesse non assumano dipendenze indirette (che è una situazione che dovresti cercare di evitare quando possibile), non passerei attraverso lo sforzo extra di creare un'interfaccia per ogni classe nel modello di dominio.

+0

@Aaronaught: Ok per ORM specifico, ora presumo che io stia usando le funzionalità FullText, userò Lucene.NET che usa anche gli Attributi. E per quanto ne so, non c'è modo di usare la mappatura degli attributi esterni come fa FluentNhibernate. Quindi qual è l'approccio migliore per mantenere le mie entità di dominio autentiche POCO? Forse le interfacce funzionano in questo modo? –

+0

@Yoann. B: Non ero a conoscenza del fatto che Lucene.NET usasse gli attributi di decoratore; sicuramente non li richiede. Se vuoi usarli, hai due possibilità: o (a) rendere il tuo modello di dominio dipendente da Lucene.NET, che sembra una cattiva idea (e se invece vuoi usare SQL FTS?) E se una versione futura di Lucene.NET supporta POCO?), Oppure (b) sposta i tuoi oggetti di ricerca e i repository in un diverso spazio dei nomi/assembly e usa uno strumento come AutoMapper per convertirli in oggetti di dominio. Ad ogni modo, non credo che la creazione di interfacce per gli oggetti sarebbe di grande aiuto. – Aaronaught

+0

@Aaronaught: hai detto che Lucene.NET non richiede attributi? si tratta di un'altra domanda che ho postato (http://stackoverflow.com/questions/2356593/lucene-net-and-poco-entities) ma come si fa senza utilizzare gli attributi con Lucene.NET? –

1

Oggetti semplici, a meno che non ci sia un'interfaccia comune per un'implementazione che può cambiare. Ecco a cosa servono le interfacce. Le classi di valore come il denaro o l'indirizzo non rientrano in questa categoria.

L'interfaccia di esempio non ha alcun comportamento al di là di getter/setter. Non è per quello che sono le interfacce.

+0

Considererei questo esempio specifico come un'entità di dominio, piuttosto che un oggetto valore, poiché identifica un utente specifico. Ma questo dipenderebbe dall'uso. –

+1

Stessa conclusione per me - entità dominio o oggetto valore, le interfacce consentono alle implementazioni di cambiare senza influenzare i client. – duffymo

6

Le interfacce sono normalmente considerate "contratti" e pertanto definiscono il comportamento. L'altro uso principale è per il mocking, in modo da poter fornire entità di dominio che erano mock-up invece di provenire da un'origine dati specifica (e con dipendenze su tale origine).

Per un semplice oggetto di trasferimento dati, non riesco a vedere un sacco di utilizzo per la definizione di interfacce, ma sono disposto a essere smentito. Andrei con oggetti semplici per questo.

1

Un'entità nella maggior parte dei casi non deve essere digitata.

Un'entità definisce esplicitamente un'identità univoca per altre entità dello stesso tipo. Un'entità Ordine dovrebbe già avere un'identità diversa da tutte le altre entità dell'Ordine nel sistema. Può anche essere dannoso per l'identità di un'entità se lo si modella tramite l'ereditarietà.

Ad esempio: Supponiamo di avere un Customer e di implementare Customer come AmericanCustomer. Dare tutto il comportamento e lo stato necessario per essere un cliente americano (suggerimento: adora fare shopping!). Poi più tardi lo stesso Customer, con lo stesso nome, le stesse abitudini di acquisto - viaggia in Giappone. Sono ancora uno AmericanCustomer? Crei un nuovo JapaneseCustomer e copi tutti i dati incluso l'ID di quel cliente in questo nuovo tipo? Ciò potrebbe avere conseguenze ..

Lo stesso cliente ama ancora lo shopping? Questo può o non può essere vero per lo JapaneseCustomer.Eppure, all'improvviso, all'interno di un solo volo prenotato, questo Customer è diverso. Altri oggetti nel sistema richiederanno lo stesso Customer utilizzando il proprio ID univoco e potrebbero finire con una diversa immagine dell'oggetto di quanto si aspettassero (uno AmericanCustomer). Forse il Cliente è modellato intorno all'origine nazionale anziché nella posizione corrente. Questo potrebbe risolvere alcuni dei problemi, ma anche introdurne di nuovi.

Modella le tue entità attorno alla loro identità. L'identità non riguarda solo i campi ID. Un modello è più di questo. Le entità dovrebbero avere un comportamento significativo sotto forma di logica aziendale, sì, ma non sono servizi. Non dovresti preoccuparti di inviare polimorficamente a comportamenti diversi. Ciò che è più importante è come il tuo sistema vede questa Entità attraverso la sua identità unica. Evita di pensare ai tipi di entità. Organizza invece le identità.

Problemi correlati