1

I'm writing the back-end for a chat application. The database being used is MongoDB, which uses objects that have their own unique IDs.

I've run into a problem; I have two document types, channels and users, and said types need to reference each other.

The user object needs a list of channels it can participate in, and the channel object needs a list of users that are participating. Without these two objects becoming accidentally desynced (a channel object having a user in its list whose own channel list does not include said channel, vice versa), how should I go about structuring the database without sorting through every user and channel every single time I need to get a user list or channel list?

I've previously tried the below model: enter image description here

...however, every time a user joins or leaves a channel, the channel must be removed from the user's channel list and the user must be removed from the channel's user list. If any error were to occur on either side and not the other, the server would have to backtrack to prevent any inconsistencies later on.

  • With a UserChannels collection? Don't mix oranges and appels in the same basket. – Laiv Feb 11 at 7:07
1

Why are there two copies of the state?

Hmm, why would the User need to hold a list of channels its participating in? This is not objectively state of the user, but of the situation that the user is in. It does make sense for a channel to be aware of Users, if only because a channel to no-where makes little sense.

My first thought would be to remove the channel list from the user. If you need to list up all channels, mongo does provide indecies over multi-valued properties of the document collection.

Two copies. What can be done?

If for whatever reason the channel list must exist in the user document and an equivalent user list in the channel, I would apply double-binding, or half-binding.

In Double binding both the Channels in the User, and the Users in the Channel must be verified against each other. Should both documents concur that a Given user is a member of a given channel, then the user is a member, otherwise its not. Unfortunately this requires N documents for verification.

In Half-binding pick either the channels, or the users to store an authoritative copy, and the others store an indicative copy. The indicative copy must be verified against the authoritative, while the authoritative stands alone. Unfortunately this does mean that there may exist an authoritative record that states membership, while the indicative copies do not know of this authoritative membership. This will necessitate a lookup (via index) for any unknown authoritative records to complete the listing. In short the indicative copy is a cache of how the system was, if regenerating the view is expensive this might be acceptable with an eventual consistency.

How about Three documents?

Considering that these two lists essentially represent the same data from different perspectives, why don't you model it as a relationship? Create a document that binds a user to a given channel. Deleting the document is atomic.

  • Need a list of Channels for a user, list by UserID.
  • Need a list of Users for a channel, list by ChannelID.

Now remove the channel/user lists from the original documents. If need be on request generate the channel/user list and embed it in the larger document and/or extract the channel/user list and update those separate documents in line with channel/user saving.

Its relational, so why not a Relational Database?

While I know the fashion these days are for document stores, there is something to be said about transactions in relational stores when it comes to relational data.

  • 1
    It's relational, so why not a Relational Database? this. – Laiv Feb 11 at 10:29

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.