I'm creating an application based on the microservice architecture and stuck on notifications microservice design. Basically it should be a service that notifies users about events happening in the app through multiple channels. Channel selection should be performed based on user preferences for particular notification type or maybe notification category, not to make it too specific. So, I decided to make notifications service responsible for managing user's notification preferences.
I use Kafka for inter-service communication and here comes the problem, I'm not sure how other services should interact with notifications service. I came up with two approaches:
- Every microservice owns some topics. E.g.
User microservice
ownsuser.account.email-update
,user.profile.update
, etc.Orders microservice
ownsorders
,orders.cancelled
, etc. No microservice produces to other's topics (single writer). Notifications microservice keeps a list of topics it should consume. Obviously, messages in this topics have different structure despite some common fields. This makes notifications service responsible for transforming heterogenous messages to some generic format that can be understood by services responsible for delivering of notifications through particular channel (SMS, messenger bot, email).
Pros:
- Services produce messages once.
Cons:
- Coupling? Notifications service should be aware of every topic's message format and how to process it. And there will be a lot.
- Services responsible for producing messages also to
notifications.*
topics. E.g.notifications.user.account
,notifications.user.profile
,notifications.orders
. This allows to generalize message structure to some extent.
Pros:
- Some message structure generalization.
Cons:
- Multiple root messages for single event.
- Shift of responsibility for notification initiation to individual services?
- Coupling again?
Notification delivery services incapsulate assets such as simplified user representation and templates. They should be able to select appropriate one based on event type, and extract notification context and provide it to renderer to get a message to send.
Both approaches look tightly coupled to me and doesn't look scalable. How can I improve this architecture to make services more scalable and maintainable?
Also I'm not sure about a separate topic for each channel, and message structure. If I go with a topic per channel, it seems logical to include recepient's email as a field into a message sent to notifications.email
topic. This makes notifications service responsible for fetching user email by ID. The same logic is applicable to notifications.messenger
topic and chat ID. And this can go out of hand quickly. Due to delivery services' having of their own simplified copies of user table, I probably shouldn't include these fields. This leads to another question: do I need separate topics for channels at all?