MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Denormalisierung mit Mongoose:So synchronisieren Sie Änderungen

Ok, während ich auf eine bessere Antwort als meine eigene warte, werde ich versuchen zu posten, was ich bisher gemacht habe.

Prä-/Post-Middleware

Als erstes habe ich versucht, die Pre/Post-Middleware zu verwenden um Dokumente zu synchronisieren, die aufeinander verweisen. (Zum Beispiel, wenn Sie Author haben und Quote , und ein Autor hat ein Array des Typs:quotes: [{type: Schema.Types.ObjectId, ref:'Quotes'}] , dann müssen Sie jedes Mal, wenn ein Zitat gelöscht wird, seine _id entfernen aus dem Array. Oder wenn der Autor entfernt wird, möchten Sie vielleicht, dass alle seine Zitate entfernt werden).

Dieser Ansatz hat einen wichtigen Vorteil :Wenn Sie jedes Schema in einer eigenen Datei definieren, können Sie die Middleware dort definieren und haben alles ordentlich organisiert . Wann immer Sie sich das Schema ansehen, können Sie direkt darunter sehen, was es tut, wie sich seine Änderungen auf andere Entitäten auswirken usw.:

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.
    )
})

Der Haupt-Nachteil Allerdings werden diese Hooks nicht ausgeführt, wenn Sie update oder andere Funktionen zum statischen Aktualisieren/Entfernen von Modellen aufrufen . Stattdessen müssen Sie das Dokument abrufen und dann save() aufrufen oder remove() auf ihnen.

Ein weiterer kleinerer Nachteil ist, dass Quote jetzt jeden kennen muss, der darauf verweist, damit es ihn aktualisieren kann, wenn ein Zitat aktualisiert oder entfernt wird. Nehmen wir also an, dass ein Period hat eine Liste mit Zitaten und Author hat auch eine Liste mit Zitaten, Zitat muss über diese beiden Bescheid wissen, um sie zu aktualisieren.

Der Grund dafür ist, dass diese Funktionen atomare Abfragen direkt an die Datenbank senden. Das ist zwar nett, aber ich hasse die Inkonsistenz zwischen der Verwendung von save() und Model.Update(...) . Vielleicht verwendet jemand anderes oder Sie in Zukunft versehentlich die statischen Update-Funktionen und Ihre Middleware wird nicht ausgelöst, was Ihnen Kopfschmerzen bereitet, die Sie nur schwer loswerden können.

NodeJS-Ereignismechanismen

Was ich derzeit tue, ist nicht wirklich optimal, aber es bietet mir genug Vorteile, um die Nachteile tatsächlich zu überwiegen (das glaube ich zumindest, wenn mir jemand Feedback geben möchte, wäre das großartig). Ich habe einen Dienst erstellt, der ein Modell umschließt, sagen wir AuthorService das erweitert events.EventEmitter und ist eine Konstruktorfunktion, die ungefähr so ​​aussieht:

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()

Die Vorteile:

  • Jede interessierte Funktion kann sich für die Serviceevents anmelden und benachrichtigt werden. So kann zum Beispiel bei einem Quote aktualisiert wird, kann der AuthorService darauf hören und die Authors aktualisieren entsprechend. (Anmerkung 1)
  • Quote muss nicht alle Dokumente kennen, die darauf verweisen, der Service löst einfach das QuoteUpdated aus Ereignis und alle Dokumente, die in diesem Fall Operationen ausführen müssen.

Anmerkung 1:Solange dieser Dienst verwendet wird, wann immer jemand mit Mongoose interagieren muss.

Die Nachteile:

  • Boilerplate-Code hinzugefügt, der einen Dienst anstelle von Mongoose direkt verwendet.
  • Jetzt ist es nicht mehr ganz offensichtlich, welche Funktionen aufgerufen werden, wenn Sie das Ereignis auslösen.
  • Sie entkoppeln Producer und Consumer auf Kosten der Lesbarkeit (da Sie einfach emit('EventName', args) ausgeben , es ist nicht sofort ersichtlich, welche Dienste auf dieses Ereignis lauschen)

Ein weiterer Nachteil besteht darin, dass jemand ein Modell aus dem Dienst abrufen und save() aufrufen kann , in dem die Ereignisse nicht ausgelöst werden obwohl ich sicher bin, dass dies mit einer Art Hybrid zwischen diesen beiden Lösungen behoben werden könnte.

Ich bin sehr offen für Vorschläge in diesem Bereich (weshalb ich diese Frage überhaupt gepostet habe).