Được rồi, trong khi chờ câu trả lời hay hơn câu trả lời của chính mình, tôi sẽ cố gắng đăng những gì tôi đã làm cho đến nay.
Phần mềm trung gian trước / sau
Điều đầu tiên tôi đã thử là sử dụng phần mềm trung gian trước / sau
để đồng bộ hóa các tài liệu đã tham chiếu lẫn nhau. (Ví dụ:nếu bạn có Author
và Quote
và Tác giả có một mảng kiểu:quotes: [{type: Schema.Types.ObjectId, ref:'Quotes'}]
, sau đó bất cứ khi nào một Trích dẫn bị xóa, bạn phải xóa _id
của nó từ mảng. Hoặc nếu Tác giả bị xóa, bạn có thể muốn xóa tất cả các trích dẫn của anh ấy).
Cách tiếp cận này có một lợi thế quan trọng :nếu bạn xác định từng Lược đồ trong tệp riêng của nó, bạn có thể xác định phần mềm trung gian ở đó và có tất cả được sắp xếp gọn gàng . Bất cứ khi nào bạn nhìn vào lược đồ, ngay bên dưới, bạn có thể thấy nó hoạt động gì, những thay đổi của nó ảnh hưởng như thế nào đến các thực thể khác, v.v.:
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.
)
})
Nhược điểm chính tuy nhiên, các móc này không được thực thi khi bạn gọi cập nhật hoặc bất kỳ chức năng xóa / cập nhật tĩnh nào của Mô hình
. Thay vì bạn cần truy xuất tài liệu và sau đó gọi save()
hoặc remove()
trên chúng.
Một nhược điểm khác nhỏ hơn là Quote bây giờ cần phải biết về bất kỳ ai tham chiếu đến nó, để nó có thể cập nhật chúng bất cứ khi nào một Quote được cập nhật hoặc loại bỏ. Vì vậy, giả sử rằng một Period
có một danh sách các trích dẫn và Author
cũng có một danh sách các trích dẫn, Quote sẽ cần biết về hai trích dẫn này để cập nhật chúng.
Lý do cho điều này là các hàm này gửi các truy vấn nguyên tử đến cơ sở dữ liệu trực tiếp. Mặc dù điều này là tốt, nhưng tôi ghét sự mâu thuẫn giữa việc sử dụng save()
và Model.Update(...)
. Có thể ai đó khác hoặc bạn trong tương lai vô tình sử dụng các chức năng cập nhật tĩnh và phần mềm trung gian của bạn không được kích hoạt, khiến bạn đau đầu mà bạn phải vật lộn để loại bỏ.
Cơ chế sự kiện NodeJS
Những gì tôi đang làm hiện tại không thực sự tối ưu nhưng nó cung cấp cho tôi đủ lợi ích để thực sự giảm bớt khuyết điểm (Hoặc tôi tin rằng, nếu ai quan tâm hãy cho tôi một số phản hồi sẽ rất tuyệt). Tôi đã tạo một dịch vụ bao quanh một mô hình, chẳng hạn như AuthorService
mở rộng events.EventEmitter
và là một hàm Constructor sẽ giống như sau:
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()
Ưu điểm:
- Bất kỳ chức năng nào quan tâm đều có thể đăng ký với Serviceevents và được thông báo. Theo cách đó, chẳng hạn, khi
Quote
được cập nhật, AuthorService có thể nghe nó và cập nhậtAuthors
cho phù hợp. (Ghi chú 1) - Quote không cần phải biết tất cả các tài liệu tham chiếu đến nó, Dịch vụ chỉ cần kích hoạt
QuoteUpdated
sự kiện và tất cả các tài liệu cần thực hiện các thao tác khi điều này xảy ra sẽ làm như vậy.
Lưu ý 1:Miễn là dịch vụ này được sử dụng bất cứ khi nào bất kỳ ai cần tương tác với mongoose.
Nhược điểm:
- Đã thêm mã soạn sẵn, sử dụng dịch vụ thay vì trực tiếp mongoose.
- Bây giờ, không rõ chính xác hàm nào được gọi khi youtri kích hoạt sự kiện.
- Bạn tách biệt nhà sản xuất và người tiêu dùng với chi phí phù hợp (vì bạn chỉ cần
emit('EventName', args)
, không rõ ràng ngay lập tức Dịch vụ nào đang lắng nghe sự kiện này)
Một bất lợi khác là ai đó có thể truy xuất Mô hình từ Dịch vụ và gọi save()
, trong đó các sự kiện sẽ không được kích hoạt mặc dù tôi chắc rằng vấn đề này có thể được giải quyết bằng một số loại kết hợp giữa hai giải pháp này.
Tôi rất cởi mở với các đề xuất trong lĩnh vực này (đó là lý do tại sao tôi đăng câu hỏi này ngay từ đầu).