Decouple SDK Code From Core React Code
And reduce the size of your future PRs with the Open/Closed Principle
Suppose I use an external service provider to process payments for my E-commerce app, and I need to embed some external SDK code to integrate the payment service into my app.
In this oversimplified example, letās say the payment service is responsible for checking whether a given payment method (e.g, Apple Pay and Google Pay) is available based on the customerās device, region, etc. While my ācoreā UI component PaymentOptions
is responsible for rendering the available payment methods as options. Lastly, I want the flexibility of adding new payment methods in the future (for šš°reasons).
I can write it this way.
However, the UI code is tightly coupled with the external code from the payment service, i.e., I have to modify the PaymentOptions
component in order to add a new payment method or to make SDK updates.
I can perhaps break out the SDK code into a separate hook.
I still have to modify PaymentOptions
and any other components that share the usePaymentMethods
hook if I wanted to add, for example, isPaypalAvailable
.
To minimize the size of future PRs, Iāve been thinking about the Open/Closed Principle, the āOā in SOLID (check out this excellent explainer): āA software artifact should be open for extension but closed for modification.ā
In my own words: I should design this feature in such a way that I donāt have to touch any of the original code I wrote (closed for modification) if I were to add new payment methods in the future (open for extension).
Hereās my take on this principle. Letās separate the payment service into its own module: a simple object where each key represents a payment method. Every payment method key points to an object with an isAvailable
property (a function that uses the SDK code) and a component
property (the UI component for the payment option).
Import paymentServiceModule
into the PaymentOptions
component.
PaymentOptions
is now decoupled from the SDK implementation details, and is ignorant of the particular payment methods.
When I want to extend this feature with a new payment method (i.e., PayPal), I simply slot in a new key/value pair to paymentServiceModule
without having to modify either the PaymentOptions
component or the original payment methods.
The UI code should in theory also be protected against modification if I were to change payment service providers (for šø reasons) as long as the payment methodās duck typing remains unchanged.
Am I applying the Open/Closed Principle correctly? Curious to learn other React or JavaScript patterns in the wild that follow this principle.
Bonus
In paymentServiceModule
, lazy load each payment option using the React.lazy
API.
In PaymentOptions
, wrap each payment option in Suspense
, to lazy load the component based on availability.