Decouple Third Party Code From Core React Code
Suppose I use a third party service provider to process payments for my E-commerce app, and I need to embed some third party 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 third party 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 third party code updates.
I can perhaps break out the third party code into a separate hook.
However, I still have to modify
PaymentOptions and any other components that share the
usePaymentMethods hook if I wanted to add, for example,
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 third party code) and a
component property (the UI component for the payment option).
paymentServiceModule into the
PaymentOptions is now decoupled from the third party 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.
paymentServiceModule, lazy load each payment option using the
PaymentOptions, wrap each payment option in
Suspense, to lazy load the component based on availability.
- A First Step to Improve Your Code Before Diving into Domain Driven Design or the Clean Architecture by Andréas Hanss
- Revisiting SOLID by Matthew Lucas
- Intro to React Server Side Rendering
- Decouple Data from UI with React Hooks
- Decouple Data from UI in React Part 2: Hooks, Render Props, and HOC patterns