Domanda

Una cosa che vedo in alcune app DDD aziendali su cui lavoro è l'uso di interfacce identiche alle entità di dominio, con un mapping uno-a-uno di proprietà e funzioni. In effetti, un oggetto dominio viene sempre utilizzato attraverso la sua interfaccia uno a uno e tutte le entità di dominio hanno un'interfaccia uno a uno in questo stile.

Ad esempio:

Account oggetto dominio:

public class Account : IAccount
{
     public string Name {get;set;}
     //...some more fields that are also in IAccount
     public decimal Balance {get;set;}
}

Ed è l'interfaccia corrispondente

public interface IAccount
{
   string Name {get;set;}
   //... all the fields in Account
   decimal Balance {get;set;}
}

Ma ultimamente mi sono sempre più convinto che questo è, in effetti, un anti-schema.
L'ho gestito da alcuni architetti della comunità open source e dicono che questo si basa su errori di progettazione o difetti, da qualche parte nella catena del design.

Quindi dico ai miei colleghi che dovrebbero smettere di creare interfacce per gli oggetti Dominio. Perché non hanno alcuno scopo e devi aggiornare l'interfaccia ogni volta che aggiorni le entità del dominio.

In primo luogo è stato affermato che queste interfacce forniscono il "disaccoppiamento", ma lo contrappongo perché, poiché le interfacce hanno una relazione uno a uno con le entità del dominio, non forniscono alcun disaccoppiamento, una modifica all'interfaccia significa un cambiamento nell'entità dominio e viceversa.

La prossima affermazione è che abbiamo bisogno delle interfacce a scopo di test. La mia contropartita è che Rhino-mock provvede al derisione e al mobbing di classi concrete. Ma sostengono che Rhino-mock abbia problemi con le classi concrete. Non so se lo compro, anche se rhino-mock ha problemi con le classi concrete, ciò non significa necessariamente che dovremmo usare le interfacce per le entità del dominio.

Quindi sono curioso:

Perché dovresti avere interfacce one-to-one per le tue entità di dominio?

Perché no?

Perché è una pratica buona o cattiva?

Grazie per aver letto!

MODIFICA : dovrei notare che utilizzo sempre le interfacce e credo che, se richiesto, userò un'interfaccia a portata di cappello. Ma mi riferisco in particolare alle entità di dominio con interfacce one-to-one.

È stato utile?

Soluzione

È una cattiva pratica come descritto, ma ...

Non esiste un motivo specifico per cui le tue interfacce devono essere diverse dalle entità del tuo dominio; a volte è davvero la mappatura giusta. Ma è sospetto che sia sempre così. Il punto preoccupante è la questione se le interfacce siano state progettate o meno o se siano state semplicemente gettate in posizione per mancanza di tempo / pigrizia.

Per usare il tuo esempio, l'interfaccia IAccount che descrivi espone getter e setter sull'oggetto Account; sembra un po 'strano e improbabile che tutto ciò che utilizza un Conto abbia bisogno di stabilire il saldo sul conto e che tale autorizzazione implicita sia specificata a quel livello di interfaccia. Non c'è posto nel tuo sistema in cui desideri semplicemente controllare ma non impostare il saldo del conto?

Altri suggerimenti

Il motivo principale per specificare sempre gli oggetti del dominio come interfacce anziché direttamente come classi è quello di darti un certo grado di libertà sull'implementazione. Nel tuo esempio hai solo un tipo di IAccount, quindi è un po 'ridondante.

E se avessi, ad esempio:

public class Account : IAccount { ... }       // Usual account, persistent
public class MockAccount : IAccount { ... }   // Test mock object
public class TransAccount : IAccount { ... }  // Account, not persistent
public class SimAccount : IAccount { ... }    // Account in a performance sim

e così via?

Definendo gli oggetti dominio come interfacce, è possibile sostituire le implementazioni senza disturbare la definizione del dominio.

In generale, se le mie lezioni non faranno parte di un modello di progettazione come Strategia o Visitatore, non aggiungo interfacce.

L'aggiunta di interfacce è davvero utile per modelli di progettazione come Strategia e Visitatore, ma in quei casi non copio carbone i getter e i setter delle classi di dominio. Invece, creo interfacce specifiche per le interfacce del modello di progettazione che creo.

interface SomeStrategy {
   void doSomething(StrategyData data);
}

interface StrategyData {
   String getProperty1();

   String getProperty2();
} 

Ciò mi consente di consentire alle classi di dominio di implementare tali interfacce o di utilizzare il modello Adapter. Trovo che questo sia un approccio molto più pulito che crea semplicemente interfacce per il gusto di farlo.

Il design dovrebbe sempre ridurre l'incertezza. Creare interfacce per il gusto di farlo non riduce l'incertezza, anzi probabilmente aumenta la confusione poiché non ha alcun senso.

Le interfacce one-to-one sulle entità sono un anti-pattern

James Gregory lo ha messo meglio di me qui .

Sono d'accordo con te. Le interfacce dovrebbero agire come un contratto, quindi non ha alcun valore avere interfacce one-to-one con entità di dominio. Può essere utile se desideri astrarre un determinato comportamento. Ma questo funzionerà in alcuni casi.

Perché questo è svanito?

Trovo che avere un'interfaccia per un oggetto dominio, come diceva Charlie Martin, mi permetta di scegliere la mia implementazione.

Un esempio fondamentale è l'identificatore (object.Id) di un oggetto, questo sarà diverso a seconda di dove lo memorizzi e la responsabilità di crearlo potrebbe o meno riposare nell'implementazione dei dati in un secondo momento. In SQL Server potresti scegliere un numero automatico, ma nell'archiviazione di tabelle di Azure potresti scegliere un Guid, ma non vuoi cambiare la logica aziendale della tua app perché cambi la posizione in cui memorizzi i tuoi dati.

Potrei o meno scegliere di mantenere persistente il mio oggetto dominio o addirittura utilizzarlo nel livello di presentazione: dipende dall'ambito della mia app che è il migliore. Ma l'aggiunta di una serie di interfacce di dominio comuni in un livello comune mi consente di scrivere servizi contro di loro e riutilizzarli ancora e ancora.

Esamineremmo lo stesso argomento su quello che dovrebbe essere un indirizzo se non avessimo IAddress, i nuovi programmatori riscriverebbero roba per carte di credito se non fosse per ICreditCard.

L'etichetta anti-pattern è un cattivo uso del linguaggio, è una semplificazione eccessiva per descrivere il valore delle soluzioni per compiti complessi e vari.

Esiste un posto per la maggior parte degli schemi, anche il singleton malignato, il che significa che non è un "anti-schema", almeno per quanto suggerisce il termine.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top