Memorizzazione direttamente nel modello

6

Normalmente quando ho alcuni dati da memorizzare, faccio qualcosa di simile

Model model = new Model();
Notifier notifier = new EventBus();
Store store = new DataStore();
model.updateTitle("new title");
store.save(model);
notifier.notifyAboutChange(model);

Ho letto molti post riguardo l'inserimento della logica nei modelli, ma le persone sembrano ancora salvare il loro modello aggiornato "fuori" dal modello che risulta nel codice come sopra, dove si notifica la modifica al di fuori del modello.

Perché non salvare direttamente nel modello? Quindi il codice sopra potrebbe essere qualcosa di simile

Model model = new Model(new EventBus(), new DataStore());
model.updateTitle("new title");
// sample model
class Model {
    Constructor(Notifier notifier, Store store) {..}
    void updateTitle(String str) {
        this.title = str
        this.store.update(this);
        this.notifier.notifyAboutChange(this);
    }
}

In questo modo ti assicuri che tutto viene eseguito ogni volta che viene richiamato l'aggiornamentoTitle e rende più semplici le chiamate ai modelli.

Un problema con questo potrebbe essere che gli oggetti sono spesso costruiti automaticamente da orms, il che significa che non hai accesso al costruttore / sono costruiti automaticamente. Questo potrebbe essere risolto iniettando nel metodo updateTitle.

    
posta Anders 31.05.2017 - 16:57
fonte

4 risposte

9

Quando usi

void updateTitle(String str) {
    this.title = str
    this.store.update(this);
    this.notifier.notifyAboutChange(this);
}

state raggruppando due operazioni in una - aggiornando il modello in memoria e rendendo persistente il modello aggiornato. Questa è una decisione politica che devi prendere per la tua applicazione. Solo tu e il tuo team siete in grado di decidere se ha senso per la vostra applicazione.

Se si decide che aggiornare un modello in memoria e rendere permanente il modello aggiornato deve essere una funzione utente, ci sono delle ramificazioni. Se hai N funzioni che aggiornano vari aspetti del modello, dovrai aggiungere lo stesso codice in tutte le altre funzioni.

Come principio, ha senso separare queste operazioni. Ciò consente all'utente di effettuare tutti gli aggiornamenti del modello a suo piacimento prima di scegliere di renderlo persistente.

Tuttavia, come hanno mostrato Google Documenti e altre applilc online, rendere persistente il modello non appena viene aggiornato dall'utente non è una cosa così brutta. Potrebbe avere senso anche per la tua applicazione.

Anche se si sceglie di adottare la politica di salvataggio del modello non appena viene aggiornato, si consiglia di utilizzare un meccanismo basato su eventi e lasciare che un osservatore renda persistente il modello non appena riceve l'evento di notifica delle modifiche.

void updateTitle(String str) {
    this.title = str
    // Let an observer listen to this event and save the model.
    this.notifier.notifyAboutChange(this);
}
    
risposta data 31.05.2017 - 18:54
fonte
2

Se le notifiche sono importanti per il livello aziendale dell'applicazione, il modello dovrebbe eseguirle autonomamente:

// Initialization
Notifier bus = new Notifier();
Repository repository = new Repository(notifier);

// Domain code
Transaction transaction = repository.openTransaction();
Model model = repository.getNewModel();
model.setTitle("new title");
transaction.commit();

Si noti come il modello stesso deve decidere se inviare notifiche, come e quando mantenere una modifica. Inoltre, il modello non è a conoscenza dell'implementazione effettiva del livello di persistenza o del bus eventi. Vengono forniti utilizzando l'iniezione delle dipendenze come dipendenze esterne.

    
risposta data 31.05.2017 - 19:18
fonte
2

È molto comune che il modello sia responsabile della persistenza stessa: si chiama pattern Active Record.

I record attivi sono super convenienti da utilizzare e possono essere generati automaticamente da un ORM. Questo approccio sembra particolarmente comune nei framework web come Ruby on Rails.

C'è una leggera differenza nel codice: un record attivo ha un metodo separato .Save() e non viene salvato automaticamente. Perché? Mentre stiamo lavorando con il modello, potremmo apportare varie modifiche ad esso. Dopo aver finito, vogliamo salvare tutte le nostre modifiche al database in una singola transazione. Se si verifica un'eccezione, non vogliamo salvare le modifiche che portano all'eccezione.

Se dovessimo salvare dopo ogni modifica, questo non solo comporterebbe molte più query sul database, ma potrebbe anche scrivere dati incompleti o non validi sul DB. Questa differenza non sembra importante quando si modifica solo una singola proprietà, ma diventa davvero fondamentale se si aggiornano più campi correlati, eventualmente anche su più oggetti.

Mentre i record attivi sono popolari, hanno un grosso problema: il modello ha bisogno di una connessione al database. Ciò significa in genere che non è possibile scrivere test unitari per il comportamento del modello senza prendere in giro il database, operazione che potrebbe essere difficile da eseguire. Separando la persistenza dal modello, ad es. utilizzando il modello di deposito è generalmente considerato molto più pulito. In questo modo, il modello rappresenta solo un'entità del modello di dominio con la sua logica aziendale intrinseca e il repository gestisce solo la persistenza del modello.

    
risposta data 01.06.2017 - 15:49
fonte
2

I have read a lot of posts about putting the logic into the models, but people seems to still save their updated model "outside" the model which results in code as above, where you notify about the change outside the model.

Sì, è così che funziona MVC. Il modello dovrebbe essere un contenitore stupido senza alcun comportamento. Tutta quell'iniezione che stai facendo e l'aggiunta di metodi per la persistenza, che è al di fuori del modello.

Perché mantenere i modelli privi di comportamento? Due grandi motivi mi vengono in mente.

  1. È possibile utilizzare il bind del modello, le annotazioni di dati e lo scaffold per popolare automaticamente i modelli dalla richiesta HTTP (ad esempio form / post variables). I binder del modello non sapranno del tuo metodo updateTitle , imposteranno solo Title . Il cielo ti aiuta se utilizzi lo stesso modello di un oggetto dominio in un aggiornamento del repository: verrà popolato con i valori della richiesta, non con l'archivio dati, e potresti finire con problemi di integrità dei dati imprevisti.

  2. Il test delle unità è molto più semplice con i modelli privi di comportamento perché formano uno strato di isolamento tra il controller e la vista. Inoltre, è uno strato che non ha bisogno di essere luccicato, sbrodolato o deriso (perché non ha alcun comportamento). Ecco perché i modelli normalmente non hanno interfacce. Ora avrai bisogno di un'interfaccia per ogni modello, in modo che tu possa stubli nei test di unità.

Se il tuo schema in qualche modo compensa i benefici del normale pattern MVC, con tutti i mezzi, vai avanti. Personalmente ti esorto a considerare la progettazione del tuo sito con modelli privi di comportamento. Conserva la logica di persistenza nel controller (e le sue dipendenze) nel luogo in cui appartiene.

    
risposta data 31.05.2017 - 21:04
fonte