Quali sono gli svantaggi del modello ActiveRecord?


30

Sono curioso di sapere quali sono gli svantaggi dell'utilizzo del modello ActiveRecord per l'accesso ai dati / oggetti business. L'unico che mi viene in mente dalla parte superiore della mia testa è che viola il principio di responsabilità singola, ma il modello AR è abbastanza comune che questo motivo da solo non sembra "abbastanza buono" da giustificare il non utilizzo (ovviamente il mio la vista può essere distorta poiché spesso nessuno dei codici con cui lavoro segue nessuno dei principi SOLIDI).

Personalmente sono non un fan di ActiveRecord (con l'eccezione di scrivere un Ruby on Rails applicazione, dove AR sente "naturale"), perché ci si sente come la classe sta facendo troppo, e l'accesso ai dati non dovrebbe essere fino alla classe stessa gestire. Preferisco usare i repository che restituiscono oggetti business. La maggior parte del codice con cui lavoro tende a utilizzare una variante di ActiveRecord, sotto forma di (non so perché il metodo sia un valore booleano):

public class Foo
{
    // properties...

    public Foo(int fooID)
    {
        this.fooID = fooID;
    }

    public bool Load()
    {
        // DB stuff here...
        // map DataReader to properties...

        bool returnCode = false;
        if (dr.HasRows)
            returnCode = true;

        return returnCode;
    }
}

o a volte il modo più "tradizionale" di avere un public static Foo FindFooByID(int fooID)metodo per i cercatori e qualcosa sulla falsariga di public void Save()salvare / aggiornare.

Capisco che ActiveRecord è in genere molto più semplice da implementare e utilizzare, ma sembra un po ' troppo semplice per applicazioni complesse e potresti avere un'architettura più robusta incapsulando la logica di accesso ai dati in un repository (per non parlare del fatto che è più facile sostituirlo strategie di accesso ai dati, ad es. forse usi Procs memorizzati + Set di dati e vuoi passare a LINQ o qualcosa del genere)

Quindi quali sono gli altri svantaggi di questo modello che dovrebbero essere considerati quando si decide se ActiveRecord è il miglior candidato per il lavoro?

Risposte:


28

Lo svantaggio principale è che le tue "entità" sono consapevoli della propria persistenza che porta a molte altre decisioni sbagliate di progettazione.

L'altro problema è che i toolkit di record più attivi fondamentalmente eseguono il mapping da 1 a 1 ai campi della tabella con zero livelli di indiretta. Funziona su piccola scala ma cade a pezzi quando hai problemi più difficili da risolvere.


Bene, avere i tuoi oggetti a conoscenza della loro persistenza significa che devi fare cose come:

  • avere facilmente connessioni al database disponibili ovunque. Questo in genere porta a fastidiosi hardcoding o una sorta di connessione statica che viene colpita da ogni luogo.
  • i tuoi oggetti tendono ad assomigliare più a SQL che a oggetti.
  • difficile fare qualsiasi cosa nell'app disconnessa perché il database è così radicato.

Alla fine ci sono molte altre decisioni sbagliate.


2
puoi approfondire "altre cattive decisioni di progettazione"?
Kevin Cline,

2
Grazie. Non ho trovato questi problemi come un problema nello sviluppo di Ruby on Rails. È ancora possibile testare il comportamento e la persistenza separatamente. L'IMO che separa la persistenza dal comportamento ha poco valore pratico.
Kevin Cline,

@kevin: queste cose hanno meno inconvenienti con caratteristiche rubino come i mixin e la tipizzazione delle anatre. Con i linguaggi statici - come C # che è quello che l'OP ha usato nella sua domanda - è un po 'più difficile separare i due.
Wyatt Barnett,

@Wayne: per me, le classi sono solo delle scatole in cui inserire i metodi: posso separare la logica aziendale dalla persistenza inserendole in classi separate, oppure posso semplicemente separarle concettualmente assicurandomi che i metodi aziendali non lo facciano io / O. In lingue con scarso supporto della delega (ad es. Java), ciò consente di risparmiare un'enorme quantità di codice. OTOH, sto solo entrando in Hibernate, quindi potrei sbagliarmi completamente.
Kevin Cline,

4
Aggiungerei due cose; 1. L'accoppiamento con il meccanismo di persistenza rende il codice difficile, se non impossibile, per testare correttamente l'unità. 2. Active Record incolla il tuo codice alle relazioni in un meccanismo di persistenza centralizzato, rendendo davvero difficile dividere il tuo monolito se mai lo decidi.
istepaniuk,

15

Il più grande svantaggio del record attivo è che il tuo dominio di solito diventa strettamente associato a un particolare meccanismo di persistenza. Se quel meccanismo dovesse richiedere un cambiamento globale, forse dalla persistenza basata su file a quella basata su DB, o tra framework di accesso ai dati, OGNI classe che implementa questo modello potrebbe cambiare. A seconda della lingua, del framework e del design, anche qualcosa di così semplice come cambiare la posizione del DB o chi "possiede" potrebbe richiedere di passare attraverso ogni oggetto per aggiornare i metodi di accesso ai dati (questo è raro nella maggior parte delle lingue che forniscono un facile accesso per configurare i file con stringhe di connessione).

In genere richiede anche di ripetere te stesso. La maggior parte dei meccanismi di persistenza ha un sacco di codice comune, per connettersi a un DB e avviare una transazione. DRY (Don't Repeat Yourself) ti direbbe come programmatore per centralizzare tale logica.

Inoltre rende complicate le operazioni atomiche. Se un gruppo di oggetti deve essere salvato in modo "tutto o niente" (come Invoice e le sue InvoiceLines e / o voci Customer e / o GL), un oggetto deve conoscere tutti questi altri oggetti e controllarne la persistenza ( che estende l'ambito dell'oggetto di controllo; i grandi record interconnessi possono facilmente diventare "oggetti divini" che sanno tutto sulle loro dipendenze) o il controllo sull'intera transazione deve essere gestito dall'esterno del dominio (e in quel caso perché stai usando AR?)

È anche "sbagliato" da una prospettiva orientata agli oggetti. Nel mondo reale, una fattura non sa come archiviare se stessa, quindi perché un oggetto codice fattura dovrebbe sapere come salvarsi nel database? Naturalmente, l'adesione eccessivamente religiosa agli "oggetti dovrebbe solo modellare ciò che le loro controparti del mondo reale possono fare" porterebbe a modelli di dominio anemici (una fattura non sa nemmeno come calcolare il proprio totale, ma suddividendo il calcolo di quel totale in un altro oggetto è generalmente considerato una cattiva idea).


In Ruby, dove la persistenza può essere definita o astratta dietro una parola chiave o un comando molto semplice, è probabilmente meno un problema. In .NET, che richiede più LoC per impostare vari meccanismi di persistenza, di solito è ridondante avere una connessione SQL per ogni oggetto, specialmente se più oggetti devono essere salvati in una transazione atomica. La connessione al DB ha lo stesso aspetto se stai salvando una fattura o un cliente. O astraggeresti le cose comuni in una classe base comune a tutti gli oggetti ActiveRecord nella tua base di codice, o lo
estraresti

puoi fornire esempi di utilizzo di Ruby ActiveRecord che illustrano i tuoi punti? Non ho riscontrato questi problemi, ma la mia applicazione era piuttosto piccola. Potrei scrivere migrazioni in Ruby e distribuirle su diverse implementazioni di DB. Ho scoperto che l'ActiveRecord di Ruby è stato molto utile per eliminare la ripetizione, quindi non vedo perché affermi che l'uso dell'ActiveRecord avrebbe l'effetto opposto. Non ho avuto problemi con il salvataggio: ho appena modificato il modello a oggetti e ho lasciato che AR aggiornasse il DB per riflettere il modello a oggetti.
Kevin Cline,

2

Lo svantaggio di base è che rende complesso il tuo modello di dominio perché contiene non solo la logica aziendale ma anche informazioni sulla persistenza.

Quindi la soluzione è utilizzare l' implementazione di ORM di Data Mapper . Ciò separa il livello di persistenza e ora siamo più incentrati sulla logica aziendale delle entità. Doctrine è Data Mapper ORM.

Ma questo approccio ha anche una certa complessità, per la query ora sei troppo dipendente da Data Mapper, rende l'ambiente orientato alle query. Per semplificarlo viene introdotto un altro livello tra il modello di dominio e Data Mapper si chiama Repository .

Il repository estrae lo strato di persistenza. Fa sentire la programmazione orientata agli oggetti nel senso, è una raccolta di tutti gli oggetti dello stesso tipo (come tutte le entità memorizzate nella tabella del database) ed è possibile eseguire operazioni su di essi come operazioni di raccolta, aggiungere , rimuovere . contiene ecc.

Ad esempio per Entità utente , ci sarà UserRepository che rappresenta una raccolta dello stesso tipo di oggetti utente (archiviati nella tabella degli utenti) su cui è possibile eseguire operazioni. Per eseguire una query nella tabella degli utenti, utilizza User Data Mapper, ma viene estratto dal modello di dominio Utente .

Il modello di repository è un tipo di livello di accesso ai dati , un altro è differenze tra gli oggetti di accesso ai dati e il repository ha la funzione di radice aggregata


I modelli di repository e DAO differiscono, DAO è l'accesso generale ai dati, Repository è per la persistenza della raccolta di tutti gli oggetti dello stesso tipo. Vale a dire che tutti i repository dovrebbero avere la stessa interfaccia (come array o elenco). L'altro re dei metodi non appartiene al repository. DAO è di livello inferiore, il repository può utilizzare DAO. Spesso i programmatori (specialmente PHP) usano il repository come DAO, ma non è corretto.
xmedeko,
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.