Ok, medan jag väntar på ett bättre svar än mitt eget, ska jag försöka lägga upp vad jag har gjort hittills.
Pre/Post Middleware
Det första jag försökte var att använda pre/post middlewares
för att synkronisera dokument som refererade till varandra. (Till exempel om du har Author
och Quote
, och en författare har en array av typen:quotes: [{type: Schema.Types.ObjectId, ref:'Quotes'}]
, närhelst ett citat raderas måste du ta bort dess _id
från arrayen. Eller om författaren tas bort kanske du vill att alla hans citat tas bort).
Detta tillvägagångssätt har en viktig fördel :om du definierar varje schema i sin egen fil kan du definiera mellanvaran där och få allt prydligt organiserat . När du tittar på schemat, precis nedanför kan du se vad det gör, hur dess ändringar påverkar andra enheter, etc:
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.
)
})
Den största nackdelen är dock att dessa krokar inte exekveras när du anropar uppdatering eller några statiska uppdaterings-/borttagningsfunktioner för modell
. Istället måste du hämta dokumentet och sedan anropa save()
eller remove()
på dem.
En annan mindre nackdel är att Quote nu måste vara medveten om alla som refererar till det, så att det kan uppdatera dem när ett offert uppdateras eller tas bort. Så låt oss säga att en Period
har en lista med citat och Author
har en lista med citat också, Quote måste känna till dessa två för att uppdatera dem.
Anledningen till detta är att dessa funktioner skickar atomfrågor direkt till databasen. Även om detta är trevligt, hatar jag inkonsekvensen mellan att använda save()
och Model.Update(...)
. Kanske någon annan eller du i framtiden råkar använda de statiska uppdateringsfunktionerna och din mellanprogramvara inte utlöses, vilket ger dig huvudvärk som du kämpar för att bli av med.
NodeJS Event Mechanisms
Det jag gör för närvarande är inte riktigt optimalt men det ger mig tillräckligt med fördelar för att faktiskt väga upp nackdelarna (eller så tror jag, om någon bryr sig om att ge mig lite feedback skulle det vara bra). Jag skapade en tjänst som omsluter en modell, säg AuthorService
som utökar events.EventEmitter
och är en konstruktorfunktion som kommer att se ut ungefär så här:
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()
Fördelarna:
- Alla intresserade funktioner kan registrera sig på Serviceevents och bli meddelade. På det sättet, till exempel, när en
Quote
är uppdaterad kan AuthorService lyssna på den och uppdateraAuthors
följaktligen. (Anmärkning 1) - Citat behöver inte vara medveten om alla dokument som hänvisar till det, tjänsten utlöser helt enkelt
QuoteUpdated
händelse och alla dokument som behöver utföra operationer när detta händer kommer att göra det.
Obs 1:Så länge den här tjänsten används när någon behöver interagera med mangust.
Nackdelarna:
- Lägg till boilerplate-kod, använder en tjänst istället för mongoose direkt.
- Nu är det inte direkt uppenbart vilka funktioner som anropas när du utlöser händelsen.
- Du frikopplar producent och konsument på bekostnad av läsbarheten (eftersom du bara
emit('EventName', args)
, det är inte direkt uppenbart vilka tjänster som lyssnar på den här händelsen)
En annan nackdel är att någon kan hämta en modell från tjänsten och anropa save()
, där händelserna inte utlöses även om jag är säker på att detta skulle kunna lösas med någon form av hybrid mellan dessa två lösningar.
Jag är väldigt öppen för förslag på detta område (vilket är anledningen till att jag postade den här frågan i första hand).