During the development of the new version of Acutario in Elixir, we had some issues related to notification dispatching.

One of the improvements we are working on is the possibility of sending notifications to multiple services, like e-mail, Slack, push notifications to mobile apps…

That functionality make us face with the problem of using different strategies depending on a concrete scenario that can be different for each user we want to notify, so we decided to develop a library that help us to manage all that cases. And we called it Ravenx.

How Ravenx works

The library is made of strategies, which define a way of sending a notification (Slack, e-mail, APNS, etc), and they have an standard interface, so its internal behaviour is abstracted from the outside.

Currently, Ravenx has two different strategies:

Slack: provides sending slack messages using webhooks URLs.

provides sending slack messages using webhooks URLs. E-mail: provides sending emails using different strategies (the ones included in bamboo).

But, as we are going to see at the end of this article, new strategies can be easily implemented to manage sending notifications to other systems and platforms.

Also, there are several ways of defining configurations which the strategies will use to know how to send the notification.

Configurations are per strategy, so they defines how a notification is sent (for example, Mailgun’s configuration to send e-mails).

Global configuration

This approach includes two kinds of configuration:

General Ravenx configuration: like the custom strategies to use, or the configuration module of our app (we will see this two aspects in a few moments).

like the custom strategies to use, or the configuration module of our app (we will see this two aspects in a few moments). Per-strategy configuration: strategy-specific configuration (Mailgun’s config, Slack’s webhook URL…)

Configuration module

A module that have a function for each strategy that receives the payload and returns a map with configurations that apply to that specific payload.

These configuration modules are useful to automatise the configuration generation, for example, if we need to retrieve the slack username of each user in each notification.

Particular configuration

This is a configuration map sent when a notification is dispatched. Useful to add configuration generated right before sending the notification.

Ravenx.dispatch(strategy, payload, %{url: "...", icon: ":bird:"})

Mixing configurations

For a given notification dispatch we can have configurations of the three kinds. In that case, configurations are merged with this priorities:

Global < Module < Particular

So a particular configuration key is used even if it’s defined in the configuration returned by a configuration module or by the app configuration.

Sending a notification

Ravenx allows to send a notification in a synchronous or asynchronous way. In case you want to send a notification in an asynchronous way, a Task will be launched to perform the operation under the hood.

To send a notification, the only action needed is a call to the library:

Sending multiple notifications

Sending a standalone notification is cool, but no one needs another library for that. The true functionality in Ravenx is sending a notification using multiple strategies in an easy way.

To do that, the library defines a macro that can be used to generate notification modules, which can be used to define how to send a particular notification using multiple strategies.

That modules must define a function called get_notifications_config that receives an object and returns a Keyword list with information of which strategies and which payloads will be used:

So that allows to define all the logic behind a notification generation in a particular module.

That modules can be used (as seen above) to dispatch the notifications in both synchronous and asynchronous way, as when we dispatched simple notifications.

Custom strategies

Anyone can create custom strategies and integrate them with Ravenx. To do that, the only requirement is to implement the interface needed and tell Ravenx which modules should use as strategies:

What Ravenx doesn’t do

Ravenx doesn’t take care of dispatching schedule (limiting the amount of notifications sent simultaneously). As Erlang VM can virtually take care of a lot (really, a lot) of internal processes, this is not a real issue.

You can send a lot of notifications asynchronously and the Erlang VM will be able to manage them without much problem.

Wrapping up

Ravenx is not the perfect solution for all the notification scenarios (and it is not intended to be that), but in Acutario we think this can solve some complex scenarios in which you can have to deal with changing configurations, multiple strategies and massive dispatches of notifications.

Using notification modules alongside with the configuration options, the code related to notifications can be organised and reused, avoiding repetitions in multiple places of your code.

If you have any question about Ravenx, you can read its documentation here, ask any question here or just open an issue! (Pull requests are welcome too :)

Hope you enjoyed this article and see you soon (in ElixirConf.EU if you assist!)