A Practical Approach to Increasing Team Autonomy with Command-Based Notifications Service

Michał Baran
XapoLabs
Published in
5 min readApr 27, 2023

Most likely, all companies that sell products send notifications of any kind to their clients. Unless the company’s primary focus is on sending notifications, customer communication is likely considered a “supporting” bounded context. This shouldn’t take up too many resources, allowing the team to concentrate on other domains of the business that generate revenue, which is known as “core” bounded contexts. The client communication bounded context, which involves sending various types of notifications such as emails, push notifications, in-app notifications, and text messages (SMS), can be implemented in multiple ways.

Every team integrates its own notification system

Integrating notifications independently in each team has advantages, such as not relying on other teams and being able to add new notifications to the team’s backlog whenever necessary. However, this approach also has a disadvantage that is not as obvious — responsibility and ownership are dispersed among all teams. It is unclear which team should take responsibility for solving issues such as exceeding the API rate limit. In a monolithic architecture, this approach may work since there is only one codebase and one SDK used by multiple teams. However, in a microservice architecture, the goal is not simply to divide the monolith into microservices but to build microservices based on bounded contexts that reflect actual business functionality in the company.

Centralized notification service

This approach offers a solution to the problem of dispersed responsibility as one team will be in charge of maintaining the service. However, it leads to a team cooperation issue where other teams might get stuck waiting on the notification service team to implement new notifications or the team that owns the notifications service have totally different priorities. It’s also not ideal to force other teams to contribute to the notification service as it takes time for them to learn and can cause bottlenecks like waiting for the code review. This can result in more engineering power being focused on the “supporting” domain rather than the more important “core” domains. Ideally, a solution that doesn’t require modification to the notification service would be preferred, allowing each team to focus on their priorities and only modify their own services as necessary. This would eliminate the need to worry about issues like external vendor API implementation, throttling, retrying, monitoring, and other related matters.

The centralized event-based notification service

In a microservice architecture, it’s common to have services communicate with each other using events. When important actions occur, like a user registering, services produce events that are sent to a queue. This can result in a large number of events covering most of the implemented business processes, which may lead to a desire to build an event-based notification service. However, teams built on bounded contexts in a DDD-aligned system aim not only to separate functionalities but also to provide autonomy for each team to make changes. There are two approaches to the event-based notification service.

The firs option is for the dedicated team that manages the notification service to handle any changes. This would require team members to grasp the intricacies of the event-producing service in order to determine when to trigger notifications. This can be a significant cognitive burden, particularly in complex domains such as cryptocurrency.

The second approach is that the notification service is owned by a dedicated team (Team B), but a member of another team (Team A) who is responsible for that sophisticated service may implement a new notification. This approach reduces cognitive load compared to the first approach, but Team A still needs to learn how the notification service works, contribute to it, and wait for Team B’s approval, which does not provide complete autonomy.

Furthermore, notifications frequently demand supplementary details such as currency and amount, and generally, all the necessary data is sent with the event. Regrettably, there are cases where the required information is missing. It is logical to include currency and amount in a sample event that reports money withdrawal. However, an issue arises when a notification necessitates a property that doesn’t fit within a specific bounded context, or when it is impossible to obtain the information at all. For instance, the current account balance is calculated in a different service after a money withdrawal, where generating another event for balance calculation is redundant.

The centralized command-based notification service

To address the issue of lack of autonomy, a command-based notification service can be used. With this approach, Team A does not have to rely on Team B for implementation or contribute to a codebase that they are not familiar with when integrating a new notification. Instead, they can produce a message to a dedicated queue, which the notification service will use to send the notification to the user. The communication can be either asynchronous (queue-based) or synchronous (HTTP-based, such as REST). The notification service will handle retrying in case of downtime and throttling if traffic increases or if the API rate limit is exceeded. Changes to the API can be made in one place, and it will be clear which team is responsible for making those changes. This approach follows the Open Host Service (OHS) pattern, where an upstream (Team B in this case) provides an open API that downstream teams can consume.

Despite the benefits, this approach also has some disadvantages. When a new type of notification is added, the notification service might need to be modified, which means that Team B will have to handle the modification request. This means that every change request will add to Team B’s backlog. However, notification channels do not change as frequently as new notifications are introduced.

Some notifications, such as OTP, are critical and must be received immediately, while for others, a delay of a few seconds or even minutes may not be significant. As traffic increases, a single queue may become a bottleneck, not due to infrastructure limitations, but due to a lack of notification prioritization. To avoid being blocked by less important notifications, such as birthday wishes, critical notifications can be sent via a dedicated queue.

If you’re interested in learning more about Xapo Bank — Bitcoin Bank providing simple and safe usability to retail customers, explore the resources at xapobank.com.

--

--