Designing a reactive-distributed messaging system, stuck on an architecture issue

TLDR; how do I handle messages to different users of my app without compromising security or forcing a non-reactive polling routine?

I am building an application in which one feature is a messaging system. Users can send messages to clients. Clients do not use the app, so messages come as text messages. Replies are handles as messages and come back the app as messages in the messaging system. Let’s introduce the concepts:

  • App instance - an instance of the app used by a user. Running on a browser.
  • Messaging service - an Akka HTTP microservice running on some server, implemented using Akka typed actors
  • Client - the client of the user using the app instance, some person with a phone number.

When a client replies to the text, the reply goes to the messaging service which has to route it as an event to the correct app instance so it shoes up there as a message. I have three options:

  1. Publish this event to a port on the messaging server, and have the app instances listen to that port; the issue is that all instances listen to all messages, a security concern as messages are private. I could encrypt them and have only the correct recipients access the decryption key, but this seems like an antipattern. I’d like them to not be able to see these messages at all.
  2. Send the message to a port on the app instance address; this solves the problem above, but introduces the problem of having to keep track of client addresses somehow. This violates REST and is generally difficult to do with things like network proxies/VPNs/other networking stuff that I don’t understand.
  3. Non-reactive; publish replies to a database on the messaging service, and have the app instances poll an API for this database periodically to check if they have mail; this is ugly and wasteful. It introduces latency between when the message was actually received and when it is available to the client, and creates a bad tradeoff where I have to decide if I want more network calls for lower latency or reduce the frequency of the network calls for a lower latency. Eats my clients’ network bills depending on their mobile coverage. Would like to avoid, but it does solve both problems above.

Is there a reactive, efficient way to do this?

Hi @nsadeh,

can you ellaborate a little more on the concept of “client”? It sounds like a plain-old SMS/MMS application on a mobile phone. Am I getting this correctly?
If yes, I think there’s a step I’m not getting. Imagine this scenario:

  • Alice and Bob are users of your App. They have a single-page application on their browser.
  • Steve is not a user of your App. Steve has a mobile phone and his number is +1 (212) 555-1234
  • Alice sends a message to Steve: “Hi Steve!”
  • Bob sends a message to Steve: “Hello Mr Smith!”

If Steve replied back, how will your service know if the reply is for Bob or Alice?
That identifier is probably what will answer your question. You could have a persistent actor behaving as an inbox for each user. The messages Steve sends back as replies would have to include the identifier for that persistent actor and so the messages would be stored there. Eventually, Alice would login and check the persistent actor for unread messages and find a test by Steve.

The browser App may actively poll the persistent entity for unread messages.

Cheers

Thanks @ignasi35.

I think you nailed it when you described Alice, Bob, and Steve. I can definitely set up mailboxes on my server. My question is about delivering them to Alice and Bob in a reactive way: I’d like the browser app to subscribe to changes in the mailbox remotely so it doesn’t have to poll. Polling makes the front end make a ton of requests and introduces a latency between when the message was received by the server. Is there a way to do this with Streams?

Hi @nsadeh,

hmm, if you don’t want polling you could use a websocket or Server-Sent Events or https://socket.io/. Then, the server side of that long lived connection would have to poll the journal for new events relevant to that user, or maybe you can use PubSub. The problem with PubSub is that delivery is not guaranteed across a cluster.

You could also use Akka Cluster’s external shard allocation to make sure the mailbox entity is on the same node where the websocket terminates. Then, the entity could have a reference to the Source representing the response channel on the websocket.

I’m just throwing some ideas, see what sticks. :-)

Hope this helps,

1 Like

Thank you! I am building a WebSockets prototype. If I end up needing to scale I’ll do either Akka Cluster or use an XMPP server.