Build flexible Rails application w/ pattern Bridge (Scale your Rails App #1)
Tags: ruby, rails, tutorial, programming
Published: 2023-10-03 13:47:24

## **Problem Statement**

You're developing your Ruby on Rails app, and you're using *SMS Factor* as your SMS sending platform. The code is running, and the SMS sending feature is operational. You're calling your SMS sending class throughout your code. Suddenly, a new opportunity arises. You need to integrate a new SMS provider, *Twilio*. But how can you add this functionality without having to modify your existing code calls?

This is a perfect use case to explore together the **Bridge** pattern. In this first edition of *Scale your Rails App*, we will explore how to use the **Bridge** pattern to overcome this challenge!

## **What is the Bridge?**

The **Bridge** pattern is a structural design pattern that allows you to separate an abstraction from its implementation so that both can evolve independently. In other words, it allows you to define an interface, called the **abstraction**, which acts as a bridge between your code and specific implementations of external services.

## **Practical Case, Switching from *SMSFactor* to *Twilio*:**

Let's imagine that you have a class called **`SmsSender`** that you're using throughout your code to send SMS messages.

```ruby
# app/lib/sms_sender
class SmsSender
  def send(phone_number:, body:)
    # logic to send SMS with SMS Factor
  end
end
```

You're calling **`SmsSender`** all over your application like this:

```ruby
SmsSender.new.send(phone_number: "0612345678", body: "Hello World!")
```

And you don't want that to change!

This is where the **Bridge** pattern becomes interesting. You can separate the SMS sending logic from **`SmsSender`** to make it *Provider Agnostic*.

So, you'll create an **`Adapter`** for each provider:

```ruby
# app/lib/sms_provider/sms_factor_adapter.rb
class Sms::Provider::SmsFactorAdapter
  # This logic was in SmsSender previously
  def send(phone_number, body)
    # logic to send SMS with SMS Factor
  end
end

# app/lib/sms_provider/twilio_adapter.rb
class Sms::Provider::TwilioAdapter
  def send(phone_number, body)
    # logic to send SMS with Twilio
  end
end
```

And now, all you have to do is change your initial **`SmsSender`** class:

```ruby
# app/lib/sms_sender.rb
class SmsSender
  def initialize(provider: SmsProvider::TwilioAdapter.new)
    @provider = provider
  end

  def send(phone_number, message)
    @provider.send(phone_number, message)
  end
end
```

And voila! The magic happens. All the places where you call **`SmsSender`** are unaware of which provider you're using to send your SMS. You can still specify which **Adapter** you want to use by providing it as an argument during the initialization of **`SmsSender`**:

```ruby
SmsSender.new(provider: SmsProvider::SmsFactorAdapter.new)
				 .send(phone_number: "0612345678", body: "Hello World!")
```

## **What's Next?**

The **Bridge** pattern is highly valuable because it significantly enhances the **extensibility** of your application.

→ If you want to add a new provider:

You only need to add a new **Adapter**. You can set it as the default adapter in the **`SmsSender`** class.

→ If you want to remove a provider:

You can simply delete the **Adapter**, without even touching **`SmsSender`**.

And all of this without modifying your calls to **`SmsSender`** in the code!

## **TL;DR**

Using the **Bridge** pattern in this situation offers several advantages:

- **Flexibility:** You can easily add new providers without modifying calls in your business logic.
- **Separation of Concerns:** The abstraction **`SmsSender`** clearly separates the SMS sending logic from the specific provider's implementation.
- **Maintainability:** Changes in one service provider do not affect the code of other providers or calls to **`SmsSender`**.

## **Conclusion**

The **Bridge** pattern is a powerful tool for managing external service providers in your Ruby on Rails application. It allows you to maintain a clean and organized codebase while facilitating the integration of new services.

I hope this article has inspired you, and you'll apply it in your Ruby on Rails projects for even more efficient and flexible results! 🚀

## **Learn More**

- Refactoring Guru's Bridge Pattern: **https://refactoring.guru/design-patterns/bridge**
- Refactoring Guru's Adapter Pattern: **https://refactoring.guru/design-patterns/adapter**