Refactoring for Scalability: From Single Route to Robust Mailing Service

Imagine deploying a new feature, only to find your application struggling under the load. That's what happened when we integrated a new mailing service into our FNSM portal backend.

This is the story of how we refactored a seemingly simple feature to handle increased load and ensure maintainability.

The Initial Implementation

Our initial approach was straightforward: when a business registered, a welcome email was sent. The logic lived directly within the registration route, creating a tight coupling between user registration and email dispatch.

// Simplified example
app.post('/register', async (req, res) => {
  // ... registration logic ...
  sendWelcomeEmail(user.email, user.name);
  res.status(201).send({ message: 'User registered' });
});

function sendWelcomeEmail(email: string, name: string) {
  // ... direct email sending logic ...
  console.log(`Sending welcome email to ${email}`); // Placeholder for actual mail sending
}

While simple, this approach had several drawbacks. First, it made the registration route slower. Second, it introduced a single point of failure: if the email service was down, user registration would fail. Third, it made testing difficult.

The Refactor: Decoupling and Asynchronous Processing

To address these issues, we decoupled the email sending from the registration route. We introduced a message queue and a dedicated email service. The registration route now simply adds a message to the queue, and the email service consumes these messages and sends the emails asynchronously.

// Registration route
app.post('/register', async (req, res) => {
  // ... registration logic ...
  enqueueEmail(user.email, user.name);
  res.status(201).send({ message: 'User registered' });
});

// Enqueue email function
function enqueueEmail(email: string, name: string) {
  // ... logic to add message to the queue ...
  console.log(`Enqueued welcome email for ${email}`); // Placeholder for actual queue interaction
}

// Email service (separate process)
async function processEmailQueue() {
  // ... logic to consume messages from the queue ...
  const { email, name } = getNextEmailFromQueue();
  sendWelcomeEmail(email, name); // Use a dedicated email sending service
}

Benefits of Decoupling

This refactoring yielded several benefits:

  • Improved Performance: The registration route is now faster because it doesn't wait for the email to be sent.
  • Increased Reliability: The registration process is no longer dependent on the email service. If the email service is down, users can still register, and the emails will be sent when the service is back up.
  • Better Scalability: The email service can be scaled independently of the registration service.
  • Enhanced Testability: We can now test the registration process and the email service independently.

MongoDB Integration

In addition to the mailing service, we also needed to register business data in a MongoDB Cloud Cluster. This involved defining a schema, creating a model, and implementing the necessary CRUD operations.

The Lesson

Decoupling services and embracing asynchronous processing are crucial for building scalable and reliable applications. By moving the mailing service to a separate queue, we significantly improved the performance and resilience of our registration process. This also allowed us to independently scale and manage the mailing service, and simplified testing and future improvements.


Generated with Gitvlg.com

Refactoring for Scalability: From Single Route to Robust Mailing Service
J

Johandev

Author

Share: