Laravel Switch Interface Implementation By Environment
Background Story
Recently i was building mobile phone number verification API that send verification code through Short Message Service (SMS) after user registration. For SMS provider our team choosed Zenziva because the API is easy to use and easy to integrate.
Unfortunately there is no sandbox mode for API so you have to top up SMS credit in order our phone number verification service is worked. But we don’t need to actually send SMS to “mock” our service so we have to figure out how Zenziva balance is not running out quickly.
Backend is using Laravel 5.3 and my environment setup is:
- Testing : Automated testing environment
- Local : My local laptop environment
- Development: Development server environment for developers
- Staging: Staging server environment for client
There are 4 different environment and as you can imagine if I used all of those environments to sending SMS using Zenziva, our team must prepare extra cash for buying extra SMS credit.
Because the goal is user get verification code, so this is become my scenario:
- Testing: Send verification code via email. Automated testing is not necessary sending real email, then change mail driver to “log” so I could easily track output in “laravel.log”
- Local: Send verification code via email. Has same scenario as testing environment but sometimes to check functionality I could change mail driver to “smtp” and use mailtrap to get verification code.
- Development: Send verification code via email. Developer should receive verification code so I used Mailtrap as mail provider.
- Staging: Send verification code via SMS. Client should receive verification code via SMS not email anymore.
As you can see I just need staging server that using Zenziva as an SMS implementation.
But how can I change the feature implementation automatically by their environment without changing the code manually?
Interface
Yes, use Interface.
Lets define an interface which has send method. Application service just need to call this interface to send SMS without knowing what should SMS provider is used.
Next I wrote to implementation classes that sending SMS via email or SMS gateway provider.
Code above is a “fake” SMS provider. As you can see, class implements SendSMS interface and send method must be exist. Then I wrote the implementation of send SMS using Mailer class.
The next one is basicly same but different in implementation. In this class send method is call an SMS gateway API as implementation different as the first one.
So in application service I only call interface like this,
By calling an interface, my service is decoupled with implementation so I could switched implementation in the background automatically.
Switch Dynamic Implementation in Service Provider
Laravel environment is set at APP_ENV variable in .env file. By checking this value, I only need to put condition in ServiceProvider as code below:
But how if suddenly I want to change implementation from email to SMS Gateway in my local environment?
Simple.
I just change my APP_ENV value in .env file to desire environment and let Service Provider change the implementation for me.
Conclusion
In some case, create an interface is usefull for decouple a service for possible different implementation like my case in this article. So if you have possibility to use more than one implementation, then interface is your choice.
Do you have other solution for this case? I will glad to see your response :)
Thank you.