MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Denormalizzazione con Mongoose:come sincronizzare le modifiche

Ok, mentre aspetto una risposta migliore della mia, cercherò di postare quello che ho fatto finora.

Pre/Post Middleware

La prima cosa che ho provato è stata usare i pre/post middleware per sincronizzare i documenti che fanno riferimento a vicenda. (Ad esempio, se hai Author e Quote e un autore ha una matrice del tipo:quotes: [{type: Schema.Types.ObjectId, ref:'Quotes'}] , quindi ogni volta che un preventivo viene eliminato, dovresti rimuovere il suo _id dalla matrice. Oppure, se l'autore viene rimosso, potresti voler rimuovere tutte le sue citazioni).

Questo approccio ha un vantaggio importante :se definisci ogni schema nel proprio file, puoi definire lì il middleware e avere tutto organizzato in modo ordinato . Ogni volta che guardi lo schema, sotto puoi vedere cosa fa, in che modo le sue modifiche influiscono su altre entità, ecc.:

var Quote = new Schema({
    //fields in schema
})
//its quite clear what happens when you remove an entity
Quote.pre('remove', function(next) {
    Author.update(
        //remove quote from Author quotes array.
    )
})

Il principale svantaggio tuttavia, questi hook non vengono eseguiti quando si chiama update o qualsiasi funzione di aggiornamento/rimozione statica del modello . Piuttosto devi recuperare il documento e quindi chiamare save() o remove() su di loro.

Un altro svantaggio minore è che Quote ora deve essere a conoscenza di chiunque vi faccia riferimento, in modo che possa aggiornarle ogni volta che una quotazione viene aggiornata o rimossa. Quindi diciamo che un Period ha un elenco di citazioni e Author ha anche un elenco di citazioni, Quote dovrà conoscere queste due per aggiornarle.

Il motivo è che queste funzioni inviano direttamente query atomiche al database. Anche se questo è carino, odio l'incoerenza tra l'uso di save() e Model.Update(...) . Forse qualcun altro o tu in futuro utilizzerai accidentalmente le funzioni di aggiornamento statico e il tuo middleware non viene attivato, causandoti mal di testa di cui fatichi a liberarti.

Meccanismi di eventi NodeJS

Quello che sto facendo attualmente non è davvero ottimale, ma mi offre abbastanza vantaggi da superare effettivamente i contro (o almeno così credo, se qualcuno volesse darmi un feedback sarebbe fantastico). Ho creato un servizio che avvolge un modello, diciamo AuthorService che estende events.EventEmitter ed è una funzione Costruttore che avrà più o meno questo aspetto:

function AuthorService() {
    var self = this

    this.create = function() {...}
    this.update = function() {
        ...
        self.emit('AuthorUpdated, before, after)
        ...
    }
}

util.inherits(AuthorService, events.EventEmitter)
module.exports = new AuthorService()

I vantaggi:

  • Qualsiasi funzione interessata può registrarsi ai Serviceevents ed essere avvisata. In questo modo, ad esempio, quando un Quote aggiornato, AuthorService può ascoltarlo e aggiornare gli Authors di conseguenza. (Nota 1)
  • Non è necessario che il preventivo sia a conoscenza di tutti i documenti che lo fanno riferimento, il Servizio attiva semplicemente il QuoteUpdated evento e tutti i documenti che necessitano di eseguire operazioni quando ciò accade lo faranno.

Nota 1:fintanto che questo servizio viene utilizzato ogni volta che qualcuno ha bisogno di interagire con la mangusta.

Gli svantaggi:

  • Aggiunto codice boilerplate, utilizzando direttamente un servizio invece di mangusta.
  • Ora non è esattamente ovvio quali funzioni vengono chiamate quando si attiva l'evento.
  • Separa produttore e consumatore a scapito della leggibilità (dal momento che si limita a emit('EventName', args) , non è immediatamente evidente quali Servizi stiano ascoltando questo evento)

Un altro svantaggio è che qualcuno può recuperare un Modello dal Servizio e chiamare save() , in cui gli eventi non verranno attivati anche se sono sicuro che questo potrebbe essere affrontato con una sorta di ibrido tra queste due soluzioni.

Sono molto aperto a suggerimenti in questo campo (motivo per cui ho postato questa domanda in primo luogo).