Domanda

Inizialmente ho progettato il mio sistema seguendo l'esempio dell'architettura s # delineato in questo articolo di codeproject (purtroppo non utilizzo NHibernate). L'idea di base è che per ogni oggetto di dominio che dovrebbe comunicare con il livello di persistenza si avrebbe un corrispondente oggetto di accesso ai dati in una libreria diversa. Ogni oggetto di accesso ai dati implementa un'interfaccia e quando un oggetto di dominio necessita dell'accesso a un metodo di accesso ai dati, codifica sempre rispetto a un'interfaccia e mai ai DAO stessi.

All'epoca, e comunque, pensavo che questo design fosse molto flessibile. Tuttavia, poiché la quantità di oggetti nel mio modello di dominio è cresciuta, mi trovo a chiedermi se qui non c'è un problema organizzativo. Ad esempio, quasi ogni oggetto nel dominio finisce con una corrispondente interfaccia di accesso ai dati e oggetto di accesso ai dati. Non solo, ma ognuno di questi è in un posto diverso che è più difficile da mantenere se voglio fare qualcosa di semplice come spostarmi in alcuni spazi dei nomi.

È interessante notare che molti di questi DAO (e le loro interfacce corrispondenti) sono creature molto semplici - il più comune ha solo un singolo metodo GetById (). Finisco con un mucchio di oggetti come

public interface ICustomerDao {
  Customer GetById(int id);
}

public interface IProductDao {
  Product GetById(int id);
}
public interface IAutomaticWeaselDao {
  AutomaticWeasel GetById(int id);
}

Dove di solito anche i loro implementatori sono molto banali. Questo mi chiedo se non sarebbe più semplice andare in una direzione diversa, magari cambiando la mia strategia avendo un singolo oggetto per semplici attività di accesso ai dati e riservando la creazione di oggetti di accesso ai dati dedicati per coloro che hanno bisogno di qualcosa di più complicato.

public interface SimpleObjectRepository {
      Customer GetCustomerById(int id);
      Product GetProductById(int id);
      AutomaticWeasel GetAutomaticWeaselById(int id);
      Transaction GetTransactioinById(int id);
}
public interface TransactionDao {
  Transaction[] GetAllCurrentlyOngoingTransactionsInitiatedByASweatyGuyNamedCarl();
}

Qualcuno ha qualche esperienza con un'architettura come questa? Nel complesso, sono molto contento del set-up in quanto ora la mia unica preoccupazione è la gestione di tutti questi piccoli file. Mi chiedo comunque quali siano gli altri approcci alla strutturazione del livello di accesso ai dati.

È stato utile?

Soluzione

Sconsiglio l'approccio semplice rispetto a quello dei sistemi semplici, di solito penso che sia meglio creare un repository personalizzato per ogni aggregato e incapsulare la logica più adatta possibile all'interno di esso.

Quindi il mio approccio dovrebbe avere un repository per ogni aggregato che ne ha bisogno, come CustomerRepository. Ciò avrebbe un metodo Aggiungi (salva) e, se adatto per quell'aggregato, un metodo Rimuovi (elimina). Avrebbe anche altri metodi personalizzati che si applicano, incluse le query (GetActive) e forse alcune di queste query potrebbero accettare le specifiche.

Sembra un grande sforzo, ma a parte le query personalizzate, la maggior parte del codice è, almeno se si utilizza un ORM moderno, molto semplice da implementare, quindi utilizzo l'ereditarietà (ReadWriteRepositoryBase dove T: IAggregateRoot) e / o composizione (chiamando a una classe RepositoryHelper). La classe base potrebbe avere metodi applicabili in tutti i casi, come GetById.

Spero che questo aiuti.

Altri suggerimenti

Lavoro in PHP, ma ho qualcosa di simile impostato per il mio livello di accesso ai dati. Ho implementato un'interfaccia simile a questa:

interface DataAccessObject
{
public static function get(array $filters = array(), array $order = array(), array $limit = array());
public function insert();
public function update();   
public function delete();
}

E poi ciascuno dei miei oggetti di accesso ai dati funziona in questo modo:

class DataAccessObject implements DataAccessObject
{
    public function __construct($dao_id = null) // So you can construct an empty object
    {
        // Some Code that get the values from the database and assigns them as properties
    }
    public static function get(array $filters = array(), array $order = array(), array $limit = array()) {};  // Code to implement function
    public function insert() {};  // Code to implement function
    public function update() {};  // Code to implement function 
    public function delete() {};  // Code to implement function        
}

Attualmente sto creando manualmente ciascuna delle classi di oggetti di accesso ai dati, quindi quando aggiungo una tabella o modifico una tabella esistente nel database, ovviamente devo scrivere il nuovo codice a mano. Nel mio caso, questo è ancora un enorme passo avanti rispetto alla nostra base di codice.

Tuttavia, puoi anche utilizzare i metadati SQL (supponendo che tu abbia una progettazione di database abbastanza solida che sfrutti i vincoli di chiave esterna e simili) per generare questi oggetti di accesso ai dati. Quindi, in teoria, è possibile utilizzare una singola classe DataAccessObject padre per costruire proprietà e metodi della classe e persino costruire relazioni automaticamente con le altre tabelle nel database. Ciò comporterebbe più o meno la stessa cosa che stai descrivendo perché in seguito potresti estendere la classe DataAccessObject per fornire metodi e proprietà personalizzati per situazioni che richiedono una certa quantità di codice costruito manualmente.

Come sidenote per lo sviluppo di .NET, hai esaminato un framework che gestisce per te la struttura sottostante del livello di accesso ai dati, come Subsonic? In caso contrario, consiglierei di esaminare solo un tale framework: http://subsonicproject.com/ .

O per lo sviluppo di PHP, un framework come Zend Framework fornirebbe funzionalità simili: http://framework.zend.com

George, so esattamente come ti senti. L'architettura di Billy ha senso per me, ma la necessità di creare un contenitore, i file Imapper e mapper sono dolorosi. Quindi se stai usando NHibernate il file .hbm corrispondente e di solito alcuni script di unit test per verificare che tutto funzioni.

Suppongo che anche se non usi NHibernate stai ancora usando una classe base generica per caricare / salvare i tuoi contenitori, ad esempio

public class BaseDAO<T> : IDAO<T> 
{
     public T Save(T entity)
     { 
       //etc......
     }
}
public class YourDAO : BaseDAO<YourEntity>
{
}

Suppongo che senza NHibernate avresti usato la riflessione o qualche altro macanismo per determinare quale SQL / SPROC chiamare?

Ad ogni modo, il mio pensiero su questo sarebbe dove DAO deve solo eseguire le operazioni CRUD di base definite nella classe base, quindi non dovrebbe essere necessario scrivere mappatori e interfacce personalizzati. L'unico modo in cui riesco a pensare a questo è usare Reflection.Emit per creare dinamicamente il tuo DAO al volo.

Sto anche usando il modello di repository per il mio DAO e sono abbastanza contento con esso. Sì, finisci con alcune classi piccole, ma è molto mantenibile. È un po 'più potente se usi l'interfaccia IQueriable (LINQ.) Puoi anche usare generici (qualcosa come T GetById<T>(int id)) se hai una struttura di database abbastanza coerente.

Il principale svantaggio del secondo approccio è il test unitario: prendere in giro metodi di fabbrica di grandi dimensioni come il tuo SimpleObjectRepository richiede più lavoro che deridere solo ICustomerDao. Ma il mio progetto si basa sul secondo approccio per oggetti altamente correlati (dove la maggior parte verrà utilizzata in qualsiasi test unitario) perché alleggerisce il carico mentale e lo rende più mantenibile.

Capirei cosa rende il tuo sistema più gestibile e facile da capire e farlo.

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