2

Exploring The Adapter Pattern In Modern Web Applications

 1 week ago
source link: https://blog.bitsrc.io/adapter-pattern-in-web-applications-995c517ff420
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Exploring The Adapter Pattern In Modern Web Applications

Reducing the Coupling Between React Components and Backend Responses

0*aWfAM0WRNKQosGcG.png

Sometimes, you’ll be given APIs to consume that you don’t really have code-level access to. For example, you might be building a frontend application that displays a summary of cryptography data via the CoinGecko API.

0*bgFretZxrunDWxzH.png

Now, in this type of app integration, you don’t have access to the API code. So, you have to make do with what you’re receiving from the API. For instance, here’s how a sample output would look like from the API:

0*MvdG2-owTN5IJa7S.png

The API returns an array of a list of coins which includes three keys:

  1. symbol

Now, your frontend only accepts a string array to display the currency list.

So, rather than passing an object array to your frontend component as props, you can instead pass a string array of currency names by manipulating the response payload.

This is where the Adapter pattern comes into play!

Pst, if you directly wanna see how this is implemented in the real world, checkout my Bit Scope.

What is the Adapter pattern?

0*QjtYPmpUsKJmaXZH.png

As shown above, the adapter simply converts an incompatible interface to a compatible form without changing the client and the consumer.

The wheels on the car isn’t fit to go on the train track. But that doesn’t mean the car should be fitted with train wheels. Instead, you can create a carrier that runs on train wheels, and carry the car on the carrier to move it along the train track instead.

By doing so, your frontend and backend aren’t tied to each other. You’re able to decouple the frontend components to your backend API and treat them as independent entities as you’re now using the adapter to pass data around your components.

Let’s look at this in detail

Consider this backend component:

import CurrencyList from './mock-currency-list';

export function getCurrencyList() {
return CurrencyList;;
}

This component returns the following Currency List:

const list: { id: string, symbol: string, name: string }[] = [
{
id: "01coin",
symbol: "zoc",
name: "01coin"
},
{
id: "0chain",
symbol: "zcn",
name: "Zus"
},
{
id: "0-knowledge-network",
symbol: "0kn",
name: "0 Knowledge Network"
},
{
id: "0-mee",
symbol: "ome",
name: "O-MEE"
},
{
id: "0vix-protocol",
symbol: "vix",
name: "0VIX Protocol"
},
{
id: "0vm",
symbol: "zerovm",
name: "0VM"
},
{
id: "0x",
symbol: "zrx",
name: "0x Protocol"
},
{
id: "0x0-ai-ai-smart-contract",
symbol: "0x0",
name: "0x0.ai: AI Smart Contract"
},
{
id: "0x1-tools-ai-multi-tool",
symbol: "0x1",
name: "0x1.tools: AI Multi-tool"
},
{
id: "0xaiswap",
symbol: "0xaiswap",
name: "0xAISwap"
},
{
id: "0xanon",
symbol: "0xanon",
name: "0xAnon"
},
];

export default list;

To keep the article simple, I didn’t quite pull data from the actual CoinGecko API, but rather copied the response payload to my backend component.

You can view the full implementation of this component here.

Now, imagine that this backend component was communicating with this React component:

import React from 'react';

export type CurrentListProps = {
/**
* a list of currencies to show.
*/
currencyList?: string[]
};

export function CurrentList({ currencyList = [] }: CurrentListProps) {
return (
<ol>
{currencyList.map((currency) => <li
key={currency}
>
{currency}
</li>)}
</ol>
)
}

As always, check out the full implementation of the component here.

Out of the box, these two components can’t really interact with each other. They’re quite disconnected, as you can see:

0*Nfz9DBPRN1BUUrld.png

However, you can do something like:

import React from 'react';
import { CurrentList } from './current-list';
import { getCurrencyList } from '@dummyorg/adapter-pattern.backend.get-currency-list';

export const BasicCurrentList = () => {
const currencyList = getCurrencyList().map((currency) => currency.name);

return (
<CurrentList
currencyList={currencyList}
/>
);
}

Now, as you can see your currency list should render as expected, and your components should be connected together nicely, as you can see:

0*3_QE4LxS09yGfU8W.png

Additionally, your React component should be working as expected as well:

0*-htoj4LCtMCJhVxi.png

But, we’re doing something wrong here.

You’re doing too much in your component. You’re manipulating the data and rendering the list in your component. Ideally, you should follow the single responsibility principle and the adapter pattern and adapt these two interfaces to work together.

This can be achieved by creating an intermediary component similar to this:

import { getCurrencyList } from "@dummyorg/adapter-pattern.backend.get-currency-list";

export class CurrencyAdapter {
private currencyList: { id: string, symbol: string, name: string }[];

constructor() {
this.currencyList = getCurrencyList();
}

adapt() {
return this.currencyList.map(currency => currency.name);
}
}

As you can see, we’ve now created a class CurrencyAdapter. This call has one method adapt that will actually map the backend API to make our component work.

You can explore its full implementation here.

So, if we consume this, our updated frontend usage will look similar to this:

import React from 'react';
import { CurrentList } from './current-list';
import { CurrencyAdapter } from '@dummyorg/adapter-pattern.adapter.currency-adapter';

export const BasicCurrentList = () => {
return (
<CurrentList
currencyList={new CurrencyAdapter().adapt()}
/>
);
}

As you can see, we no longer manipulate the data shape in the component, but rather invoke the adapter to do its designated job. And, if you observe the output, you’ll notice that it’s working as expected:

0*J79p9K77KMBOKeQm.png

And, if you check out the component tree, you’ll see that the frontend and backend are no longer connected to each other. Instead, they communicate with each other via the adapter:

0*kT3FYlfArLjd4eJg.png

As you can see, we no longer couple the frontend and backend together. Instead, we can independently work on the backend and the frontend without worrying about breaking the two with incompatible interfaces as we’re using this adapter.

Wrapping Up

And, that’s pretty much it. That was an interesting approach to decoupling software components isn’t it?

Give this pattern a try in your projects and see if you can reduce the coupling in your frontend and backed to improve the overall maintainability of your project.

If you wish to checkout the full implementation of this article, check out my Bit Scope.

I hope you found this article helpful.

Thank you for reading.

Learn More


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK