Introducing ShopBack Lite
ShopBack currently operates in six different countries: Singapore, Malaysia. Indonesia, Philippines, Taiwan and Thailand. Everyday, millions of customers use ShopBack to make smarter shopping decisions and earn cashback. However, there are many external factors that negatively impact their ShopBack experience, including slow mobile networks, pricey data plans and low-end devices.
Today we are rolling out ShopBack Lite with faster loading times, minimal data usage and better compatibility across all mobile browsers. The launch time of ShopBack Lite is 3 times faster, navigation is snappier across the entire site, and now you have full access to all our features with less than 2MB of data usage.
Architecture in a Nutshell
ShopBack Lite is a Single Page Application with a simple Node.js server that handles proxying requests to backend microservices, constructing initial application state and rendering initial HTML content. This approach is commonly-known as an isomorphic or universal React application. Benefiting from this isomorphic architecture, ShopBack Lite gains faster load times, no-page-refresh navigation and control over SEO.
On the client side, this universal JavaScript application is built on React, Redux, Babel, Webpack, Jest and Yarn. By leveraging these excellent open-source libraries, we are able to spend more time improving the user experience and applying engineering best practices to solve ShopBack-specific problems.
Async Redux Action
Handling async redux actions has always been a pain for server-side rendering because it is difficult for the server to determine the correct async actions to dispatch based on a single URL.
To solve this problem, we attached a thunk.js file to each page level component. This makes it straightforward to get the right async actions based on the current URL.
routes.js
HOME: {
component: () => import('../../views/home'),
path: '/',
thunk: createAsyncThunk(() => import('../../views/home/thunk')),
}
views/home/thunk.js
import action from './action';
const thunk = (dispatch, getState) => {
const state = getState();
if (!process.env.IS_SERVER) {
dispatch(action.getCampaigns());
}
return [
dispatch(action.getBanners({
visibleToMembers: state.app.isLogin,
})),
dispatch(action.getCategories()),
dispatch(action.getStores()),
dispatch(action.getPage()),
];
};
export default thunk;
In this way, the thunk file could be used on both server and client. In addition, we are able to differentiate async actions to be dispatched only on client or server, allowing us to fetch the bare minimum data for initial screen rendering and improving server response time.
Data Management
In ShopBack, our microservices backend exposes RESTful APIs which only return JSON objects. In some cases, API responses cannot be used for frontend rendering directly. Hence, frontend needs to take care of data aggregation and transformation so that the UI components will not be coupled with any specific data structures.
To address this issue, we introduced two concepts: selector and formatter.
Selector is responsible for aggregating atomic data resources into a complex business data model. Those models can be shared across views to eliminate duplicate requests and help user save on data consumption.
Formatter is responsible for pure data structure transformation like filtering needed properties and restructuring them.
By combining selector and formatter, we achieve a cleaner, clearer data flow in our application.
import { createSelector } from 'reselect';
import formatter from './formatter';
const categorySelector = state => state.allStores.categories;
const getCategories = createSelector(
categorySelector,
categories => formatter.categoriesFormatter(categories),
);
export default {
getCategories,
};
Reusable Components
Everything is a component in React, but each component has its own level, for example page level, business level or UI level component. To achieve a good balance between better reusability and lower complexity, each level has a specific focus.
For page level components, we spend less effort on reusability and focus on reducing complexity. For example, there can be three different login/signup pages in the same application. Though they looks similar, how users input their account details vary. If you want to reuse one component for all the variations, this component could potentially contain at least three different sets of business logic, resulting in reduced code readability and maintainability.
Business level components will be used across pages but are different from UI components, as they contain some application-specific business logic, such as Deal components which contain expiry date countdowns and copy coupon code functionality. For business level components, our requirement for its reusability is only across the current application. We do not expect consumer-facing application's business components to be used within our internal content management system unless they can be reused without any changes. If there are any changes required, we create another component instead of adding business logic to the existing one.
Lastly, UI components have the highest requirement for reusability because they need to be shared across all client frontends. They are the fundamental patterns for all frontend projects in ShopBack. Accumulating high quality UI components is one of the best ways to improve a frontend team's productivity. They server as higher level abstractions of all tedious/repetitive design and frontend code, as well as helping the team keep the user interface consistent across all platforms and projects.
Looking ahead
As ShopBack is growing rapidly, keeping ShopBack Lite fast at larger scale is a big challenge for all teams, including product, design, data and engineering. We're excited about the progress we have made and are looking forward to experimenting with valuable technologies like TypeScript and GraphQL in future sprints.
In the meantime, we are also working on consolidating our UI components into a library named SEA UI.
Starting from Southeast Asia, ShopBack aims to serve more users across the regions. We hope SEA UI could be the first cross-browser, ecommmerce-focused UI pattern library for both mobile and desktop with full i18n support. Stay tuned for more updates.
Lastly, special thanks to all of our awesome team members involves in ShopBack Lite: David, Sathappan, Gerald, Vara, Tu An, Lingjun, Gaoming, NK, Hanh, Jacob, Aung and Kevin.
We're hiring! If you are interested in building next generation Node.js applications, full-stack React applications and cutting edge content management systems, please visit our career page and get in touch with us today!
Hi Alan, great initiative and looking at your post, we could potentially help ShopBack further optimize the user experience. https://www.dynatrace.com/blog/real-user-monitoring-support-fetch-api-now-available/
Good job Alan Wei!