To understand the usage of facades on real-life projects, let's imagine that we'll implement an integration with a third-party SDK like Stripe to allow payments on our system. All cool, right?
Ok but... What is the problem with this?
Since we need to implement this SDK, the first thing that we can think of is to install the gem and just use it on our controller methods, right? Let's imagine that this gem provides the following class:
class AwesomeStripeSDK
def identify_user; end
def register_card_for_user; end
def pay; end
def refund; end
def apply_discount; end
def cancel_payment; end
def call_the_cops; end
....
end
Can we agree that this is a big class with an unknown number of parameters, right?
Although it's easy to just read the docs and implement the correct methods with the correct parameters on our controller, it can become a little monster pretty easily, simply because we don't have much control over this whole AwesomeStripeSDK
class.
This gem could provide multiple classes that need to be used together for a single action or even provide a very confusing and messy set of method names, parameter structures, etc.
The solution ? Facades!
Now that we understand the problem, it's pretty simple to propose a solution: just create another class!
The idea behind a facade is to simplify the implementation of a library or framework and also control the general structure of this service class.
For example, given the example AwesomeStripeSDK
third party class, we can define a facade like the following:
class PaymentFacade
def self.pay(price:, card:, user:)
# identify our user
AwesomeStripeSDK.identify(user)
# register the payment method
AwesomeStripeSDK.register_card_for_user(user, card)
# apply a fixed discount
AwesomeStripeSDK.apply_discount(40)
# pay the bill
AwesomeStripeSDK.pay(price)
end
end
Now on our controller, we just need to call the PaymentFacade.pay
passing the correct parameters, and we can ensure that the payment is going to be made correctly without worrying about the specific SDK API.
Another obvious advantage of this is that we can use the same facade on multiple classes in separate contexts and also write great tests since the scope is isolated. Cool, right?