L'uso del modello di strategia per i metodi di persistenza rende "okay" l'inclusione dei metodi CRUD in un oggetto di business?

6

Vorrebbe iniziare dicendo: lo sto chiedendo come un esercizio puramente accademico; Sono abbastanza contento di continuare a utilizzare il pattern del repository .

Ho sentito dire "Ho inserito i metodi di persistenza nei miei oggetti di business perché quando persistere era parte integrante della logica aziendale", che non credo sia un motivo valido, perché ciò significa solo che il tuo oggetto business ha bisogno di un TimeToSave evento

Ma si potrebbe obiettare, se si utilizza il modello di strategia per i metodi di persistenza, tecnicamente non si include la logica di persistenza nel proprio livello di dominio. Vedi esempio sotto:

class MyEntity
{
     private ISaveMyEntity _saver;
     public MyEntity(ISaveMyEntity saver) { _saver = saver; }

     public void Save()
     {
         _saver.Save(this);
     }

     //Real business methods
}

Includere con un oggetto business, i suoi metodi di persistenza, potrebbe rivelarsi conveniente. Inoltre, il pattern del repository è spesso abusato e finisce per contenere tutta la logica aziendale invece del solo CRUD, mentre l'oggetto business si trasforma in DTO; a mio parere, questo è peggio di un oggetto business contenente i metodi CRUD.

Quali potrebbero essere le potenziali insidie di includere l'accesso alla persistenza in un oggetto aziendale tramite il modello strategico? L'uso del modello strategico potrebbe attenuare alcuni dei problemi comunemente associati al codice di archiviazione in un oggetto aziendale?

    
posta TheCatWhisperer 18.04.2017 - 20:13
fonte

3 risposte

8

Hai oggetti valore che rappresentano la tua logica aziendale, contenenti regole di business. Ora stai aggiungendo un meccanismo per mantenerli. Giusto. Che ne dici di aggiungere un meccanismo per leggere un database e costruirlo da esso? Forse un metodo Load statico che prende come valore l'istanza IGetMyEntity come parametro?

Mentre ci sei, forse è meglio aggiungere qualche altro metodo:

  • RenderAsHtml ,
  • ToString ,
  • ToJson ,
  • ToXml .

Vedi dove sto andando con questo? Tutto inizia con un semplice metodo Save , ma pochi si impegna dopo che i tuoi oggetti improvvisamente sanno tutto, hanno una bassa coesione, tutto è stipato in un unico punto e impossibile da riutilizzare senza trascinare con sé il resto della caldaia indesiderata.

Il modello di repository è diventato molto popolare perché si adatta bene. Se l'oggetto di cui si sta parlando dovrebbe essere una rappresentazione in memoria di una regola aziendale, NON è consentito includere i metodi CRUD come parte della sua API.

Per espandere la tua opinione sugli DTO, quando stai facendo qualche operazione, dove generalmente hai bisogno di una validazione delle regole aziendali? È su letture? Molto probabilmente no. Puoi vietare l'accesso ad alcune risorse, ma è più un problema di autorizzazione che una regola aziendale.

Il luogo in cui è probabile applicare le regole di business è quando i dati entrano nel tuo sistema (sia esso completamente nuovo o attraverso un aggiornamento). In quell'occasione è necessario verificare che lo stato di alterazione dell'operazione del sistema sia valido, ovvero. può essere eseguito Per questo è necessario utilizzare oggetti valore o entità che applicano i vincoli. Il repository in realtà non è un posto dove contenere la logica di business. L'obiettivo di un repository è di astrarre la persistenza degli oggetti, non di imporre regole aziendali. Gli oggetti passati al repository DEVONO assicurarsi che siano validi prima di essere passati a un repository per un'ulteriore elaborazione.

Inoltre, è meglio non guardare un repository come una classe conoscendo tutte le operazioni CRUD, ma piuttosto come un livello all'interno dell'applicazione. Quando la tua applicazione cresce, è probabile che avrai bisogno di più rappresentazioni di un'entità sulle letture. Se hai un repository come una singola classe che ha più metodi di Get* , non sembra giusto e la classe potrebbe diventare abbastanza grande e difficile da mantenere presto - proveniente da un'esperienza personale.

    
risposta data 18.04.2017 - 22:08
fonte
6

Ci sono molte ragioni per cui le DTO vengono utilizzate per la persistenza, ma forse la più cattiva è transazioni di database . (vale a dire dove sono necessari più passaggi e non è possibile eseguire solo un singolo UPDATE , INSERT , DELETE , ecc.)

Alcuni dei requisiti di base per le transazioni sono i seguenti:

  • Non è possibile eseguire più passaggi con una singola istruzione SQL netta
  • No COMMIT succede fino a quando tutti i passaggi sono riusciti
  • Se un passaggio fallisce, si verifica un ROLLBACK per annullare l'intera transazione e lasciare il database nel suo stato precedente - nessun aggiornamento parziale o record orfani.

Considera gli oggetti di business nella tua applicazione:

  • Gli oggetti di business supportano il comportamento dell'applicazione
  • La loro durata sarà necessariamente collegata al loro comportamento
  • Possono contenere stati che non dovrebbero essere riflessi nel database

Consideriamo ora lo scenario in cui tutti i dati vengono inseriti direttamente nei tuoi oggetti di business dal database - come esegui una transazione complessa che potrebbe richiedere più query e istruzioni UPDATE / INSERT / DELETE?

  • Dove metti la logica della transazione? Se coinvolge più oggetti business, chiaramente non appartiene a nessuno di questi oggetti.
  • In relazione al precedente punto elenco, quale oggetto è responsabile del commit o del rollback quando un commit o un rollback influisce sui dati di più oggetti business?
  • Come gestisci le transazioni per le relazioni tra gli oggetti di business che sono rappresentati come molti a molti nel database? (ad esempio Studente-Insegnante)
  • Che cosa accade se la transazione viene annullata e deve essere ripristinata? Gli oggetti dominio sono legati al tuo comportamento, quindi non puoi semplicemente buttarli via, ma contengono i dati della transazione fallita.
  • Cosa succede se i dati che erano stati memorizzati nella cache nell'oggetto dominio non erano sincronizzati con il database?

Il problema chiave è questo: Gli oggetti business non sono transitori. Aggiungendo dati in essi, i loro dati diventano implicitamente parte del loro stato perché hai memorizzato nella cache quei dati e la durata della cache è uguale alla durata dell'oggetto business.

Perché i DTO risolvono questo problema?

  1. Se un oggetto business utilizza un DTO per i suoi dati, i dati non fanno parte del suo stato. Allo stesso modo, un oggetto business potrebbe desiderare dati da più DTO e anche questo va bene.
  2. I DTO sono oggetti transitori per loro natura; la loro vita non è legata al comportamento della tua applicazione.
  3. I tuoi oggetti di business non devono preoccuparsi della durata di un DTO per i suoi dati; se uno viene buttato via, tutto ciò di cui hai bisogno è una nuova query nel database per ottenere una nuova brillante, fresca, pulita.
  4. Puoi scrivere una transazione con DTO senza fare confusione con lo stato dei tuoi oggetti di dominio, perché i tuoi dati di transazione non fanno parte di alcun oggetto di dominio.
  5. Se la transazione fallisce, butti via tutti i DTO (quelli sono oggetti transitori comunque e sarebbero stati gettati via dopo il successo). I tuoi oggetti di business non devono preoccuparsi di rimanere in uno stato potenzialmente non valido.
  6. La tua applicazione può scegliere quali dati memorizzare nella cache in base alla presenza o meno di prestazioni dal caching; invece di "tutti" i dati vengono memorizzati nella cache per impostazione predefinita.
  7. Non è necessario implementare l'invalidazione della cache separata o la risincronizzazione con il database per ogni singolo oggetto di business.
  8. Quando i dati non vengono memorizzati nella cache dagli oggetti del dominio, qualsiasi comportamento correlato a tali dati verrà sempre interrogato direttamente dal database, pertanto è possibile lavorare con gli ultimi dati.
  9. Infine, quando è necessario scrivere una logica di transazione che coinvolge più entità, non è necessario preoccuparsi di quale dei propri oggetti di business appartiene perché gli oggetti di business non sono più legati ai dati.

Conclusione - una volta che la durata dei tuoi oggetti di dominio comportamentali determina la durata degli oggetti che leggono e scrivono nel tuo database, introduce problemi di organizzazione del codice, rollback delle transazioni , invalidazione della cache e sincronizzazione dei dati.

Mentre può sembrare "pulito" quando i diagrammi sono pieni di oggetti business ben definiti che hanno ciascuno una strong identità, facilmente riconducibile al dominio del problema, e che incapsulano tutti i propri dati e comportamenti in un unico luogo , la realtà delle interazioni multi oggetti e la complessità della gestione dei dati persistenti in un database esterno è tutt'altro che pulita.

    
risposta data 18.04.2017 - 23:16
fonte
3

Mi sembra che questo potrebbe potenzialmente rendere gli oggetti scomodi da usare se non fosse necessaria la persistenza. Al test unitario, avrei dovuto prendere in giro le interfacce di persistenza; con un sistema basato su eventi, posso semplicemente scegliere di non iscriversi all'evento SaveMe.

Un altro potenziale problema è che fare le cose in questo modo potrebbe rendere le cose potenzialmente difficili nell'istanza di fare operazioni CRUD batch efficienti.

    
risposta data 18.04.2017 - 20:17
fonte