Consider a mobile chat app with MongoDB for chat message storage and https://ably.com/ as the pub sub broker. When a new user joins a chat room, or an existing user re-install their app, old messages should be loaded.
I am considering two architectures, see the image below. One where the actual chat messages are sent via the message broker (A), and another where the message broker is only used to notify the clients that changes have occurred and all messages are loaded via REST (B).
My main question is how to best sync existing chat messages to the clients. Both approaches assumes the client generates a unique ID for each new message, so we can figure out the delta between the MongoDB store and the client at any time.
I feel option A is the better architecture, but it would be awesome to get some input on it. Is there a better approach than any of A or B?
The app will support images, videos and share cards (Open Graph). My plan is to include these as markup (embedded URLs) in the chat message, and then have the client load the actual image/video/share-card binaries from separate services.
I am aware the answer to this question may be a bit opinionated, but I am curious to hear if anyone out there as worked on something similar and what your recommendations are. Any links to blog posts on the matter is also helpful (I have googled quite a but but haven't found any specific information).
so I’ve got a few recommendations on this structure (with the caveat I’m an employee at Ably). I’d generally recommend following the rough pattern of A (going through Ably), but with a few extra elements potentially depending on the sorts of features you’d be looking for.
Authentication + Security
Ably has a robust Authentication system, allowing for fine-grained access with Tokens. This means you can easily dictate who should have access to what channels and with what permissions. This means you can by default define which users should be able to see who’s in a chat room (presence permission), who can send messages (publish permission), and who can receive messages (subscribe permission).
If you use Ably as your core means of communicating with your storage, then you can effectively use Ably to enforce whatever authentication structure you’d like. If a user can’t publish to room X, then they won’t be able to send messages to your other users nor to the MongoDB instance.
If you’re using Ably Integrations to automatically send messages to MongoDB from Ably, then you can choose to encrypt these messages with an Ably API key, which means you’ll be able to determine if a message has been actually sent from Ably or not, ensuring you can enforce an Ably-only authentication architecture.
Extensibility
As you can have messages sent to an Ably channel sent on to any number of clients and endpoints, having the core communication go through Ably can be handy for ensuring the scalability of your solution in the future.
For example, imagine you want to allow users to create plugins in the future for your chat app, if you’re having currently all your users publish directly to MongoDB you’ll need to create an entirely new mechanism for getting data from the clients to each plugin. If the messages are going through Ably, however, you can just allow for these plugins to subscribe directly to a message or create an Ably Integration connection to send messages from a certain channel or namespace in Ably onto another system.
This is great as it makes it simple to add and remove endpoints at will, without worrying about any of these changes potentially interfering with existing functionality.
Message processing
One question I’d pose would be whether you’d ever want any form of filtering, moderation, or anything else applied to messages as they come in, or more complex message grouping outside of ‘send message to channel X, users get a message from channel X’.
If you would, then for either of these architectures you’d likely need some intermediary point for processing messages, enforcing bans on communication, and such. A common way of doing this would be to create potentially Serverless Functions for certain processing steps (say ‘remove banned words’), which Ably Integrations would send any messages from Ably Channels to. As you’d not want the initial message sent to necessarily be sent to everyone as it may need to be changed/removed, you’d want separate channels for ‘sent’ messages, and messages to actually be sent onwards for storage/users to consume. You could structure this in terms of Ably channels as just
chatroom:myroom:input
andchatroom:myroom:output
. Messages into an input channel would go on to endpoints for processing, and then messages post-processing can be sent to output to be sent to clients and into storage.This again allows for you to have a fairly extensible system, where you can insert in various processing elements and mix and match them, whilst not then needing to change the overall structure of your app too much.
I’ve been part of working on a template chat application using Ably, so you may find giving that a glance may be handy for some ideas on the structure.
There’s an initial blog piece giving a summary of what we’re hoping to eventually include, and some ideas on the structure we’re looking at in there.