Don’t block your users! Why you should adopt event-driven architecture
Tags: javascript, api, webdev, typescript
Published: 2023-09-20 08:51:04

Building event-driven applications offers a flexible, but markedly different approach to building applications. Let’s take a look at how an event-driven architecture can benefit your users, developers, and your application code.

## What does event-driven mean?

Instead of building out complex, nested function calls to handle your application logic, you move to a world where everything is triggered by an event.

Imagine a user applies for a job on your platform. Usually, this would involve a `POST` request, with a lot of steps (e.g. parsing the resume, notifying the job poster, sending a confirmation email, storing the application details, handling internal analytics, etc) before the user receives a response. This feels slow, and could lead to frustration with the wider platform.

With event-driven design, many of these steps happen in the background. The heavy lifting is done behind the scenes, making things faster for the user.

## The benefits of event-driven applications

Let's consider a flow every application has to deal with — user sign-ups. When a user signs up to your service, you'll want to do a few things.

1. Create their details in the database;
2. Send a welcome email;
3. If they're a paid customer, activate any account features.

Let’s take a look at the benefits an event-driven architecture lends to this flow.

### Built-in error handling

With event-driven applications, errors not only become less of a problem, but also something you no longer have to fear.

Taking the example of a user sign-up above, let's explore what that might look like in code.

```jsx
// https://api.acme.dev/users/signup

const { email, password, type } = request.body;

// Create the new user in the database
const userRecord = await db.users.create({ email, password, type });

// Attempt to send a welcome email
await emails.send(email, 'Welcome.......');

// Activate customer payment account
if (type === 'pro') {
  const customer = await stripe.customers.create({ /* ... */ })

  await db.users.update(userRecord.id, { customer_id: customer.id })
}

return { status: 200 }
```

In this example, if the email provider is down, this whole flow stops. The only way to prevent that being a problem, is by carrying on regardless whether or not the email fails to send, meaning some users may never receive that important, friendly welcome email. You might put the email send method in a try catch block, for example, to prevent the email send from blocking the next action. But that doesn’t solve the problem entirely.

```jsx

// https://api.acme.dev/users/signup

// ...

try {
  await emails.send(...)
} catch (e) {
  console.error(e);
  console.log('no email for you, sorry');
}
```

In an event-driven application, once the new user is safely stored in the database, you can hand the rest of the process off to your event platform (ahem, Sailhouse), and let that take care of the rest. What’s more, this means you write less application code.

```jsx
// https://api.acme.dev/users/signup

const { email, password, type } = request.body;

// Create the new user in the database
const userRecord = await db.users.create({ email, password, type });

// Send an event to Sailhouse, where we'll take care of the rest
await sailhouse.publish('user-created', userRecord);

return { status: 200 }
```

When you send the event, if the `user-created` topic doesn’t exist, Sailhouse will create it for you. When you come to create subscriptions for topics in the future (i.e. things that happen after Sailhouse processes an event), you also have the option to process historical events in the topic, so no data is lost. Sweet!

![A screenshot of the Sailhouse dashboard showcasing an example user-created topic, with a single send-welcome-email push subscription pointing to an API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbvvp017n9g0pgay6yy3.jpeg)

With our `user-created` topic created in Sailhouse, let's focus on sending that pesky welcome email, by setting up a push `send-welcome-email` subscription which points to a handler sending the email, which might look like the code below. 

```jsx
// https://api.acme.dev/emails/welcome

const { email } = request.body;

await emails.send(email, 'Welcome to ACME dev!');

return { status: 200 }
```

If your email provider is down, Sailhouse will retry up to five times over the next 30 minutes and then eventually dead-letters the event ([dead letters](https://docs.sailhouse.dev/concepts/subscriptions/#dead-letters) are events that are on-hold as they couldn’t be delivered as normal). You can then re-queue the event when the provider is back up again, or if you've implemented an alternative.

![A screenshot of the Sailhouse dashboard showcasing the send-welcome-email subscription, showing a single successful event having been delievered](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zs8ntg74bax3nfeahc02.png)

By decoupling your application logic in this way, your customers benefit from a more reliable experience, while you have an almost self-healing application that minimises data-loss and prioritises as strong UX for your users.

### Faster applications for users

Focusing again on the idea of decoupling, event-driven applications can also *really* speed things up for your users.

Using a traditional architectural approach, when a new user signs up to your product, they must wait until every action has been completed until they can start using your application. Why should they have to wait until the welcome email has been sent?

When you use event-driven architecture, you massively reduce the time it takes for a user to start exploring your product after sign up. Sure, *some* logic needs to be blocking, but now, not all of it has to be.

Performance is key to a happy customer base, and this is an effective way to ensure they always have a snappy experience.

### Complex applications with simple code

Complex code isn’t necessarily a bad thing. When building a product, you have to make sacrifices in order to ship fast and deliver value. This applies to both the early stages when you’re establishing product market fit, and also to a more mature product which might have thousands of successfully onboarded customers.

But complex code can make it more difficult to add new features, or rework existing ones. And that’s where an event-driven architecture shines.

Let’s return to our `user-created` topic, and say you want to start checking emails in case they’re flagged for spam, and flag them internally for manual review. You can do this in Sailhouse by setting up another push subscription, and start processing events straight away.

![A screenshot of the new subscription form in the Sailhouse dashboard, setting up a new spam-checking push subscription](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tg6101yrs5zhcrsz6kx4.jpeg)

This extra functionality is fully isolated, with no modifications to your sign-up code, and everything can be processed in the background without disrupting the flow of your users.

This is one of the huge perks of going event-driven, it makes it **way too easy** to add new features.

## Sound good?

[Sign up to Sailhouse](https://app.sailhouse.dev) — it’s free! (while in beta). You should also check out our growing [Discord community](https://discord.gg/UyvsfJUE93), and swing by the [GitHub discussions](https://github.com/orgs/sailhouse/discussions) to talk through bugs, feature requests and all fun software things.

Happy sailing!