This project is the implementation of BBC Coding Challenge. It is implemented as 2 stand-alone apps:
Note: since both React and Rails apps are running on port 3000 by default, please run Rails app on port 3001 to prevent conflict rails s -p 3001
.
- How to install and run ReactJS frontend app
- How to install and run Rails backend app
- How to use the apps
- React Components
- Rails Backend
- Trade-offs
- Production Readiness
- App Screenshot
- Download from the source code
npm install
oryarn install
to install dependencies.npm start
oryarn start
to run app on port 3000.
- Download from the source code
bundle install
to install gemsrails db:create && rails db:migrate
to prepare databaserails s -p 3001
to run app on port 3001
Fetch and Populate Data
button will fetch data from GitHub endpoints and populate into our Rails databaseShow Price List
button will fetch items data from Rails server and present it on table format.Price Change by Category
allows users to select a category and apply percentage increase or decrease to change US and JP price within the selected category.Price Change by SKU
allows users to select an individual item and change the US and JP prices based on percentage change or new nominal values.Increase US price for selected checkbox items by 10%
allows users to select a few items by clicking the checkboxes inside items table and automatically increase the US price by 10%.
The frontend apps are composed of the following components:
App.js
is the app container and entry point of the frontend app./helpers/API.js
has the functionality to fetch data from GitHub endpoints and populate into Rails backend database.- It implements React
componentDidMount()
lifecycle to fetch data fromgithubusercontent
endpoints. - Once the data has been added, we'll store them in state management.
- We use
axios
library to send and retrieve data from Rails backend service. buildItems()
andcategorizeItem()
functions will flatten the different dataset intoitem
structure according to our Rails backend API service.buildStores()
andbuildCategories()
populate data into respective tables.
- It implements React
PriceList.js
will fetch data from our database and present it in a table format.- We also implement
componentDidMount()
to grab items data from Rails database. - Once the data is loaded, we'll store in the state and render the data in table format.
- It contains a child component
PriceListChange
that is reusable forPrice Change by Category
andPrice Change by SKU
functionality.
- We also implement
PriceListChange
is a form to adjust price list.- This component is used by
Price Change by Category
andPrice Change by SKU
from it parent component. - The parent component will pass different
props
(ie.category
oritem
) so this component can correctly send the correct action to the backend service.
- This component is used by
- Rails backend service allows CRUD operations to populate
items
,categories
, andstores
from our frontend app. - We simplify our database schema by creating
items
table with the following columns:category
,sku
,us_name
,us_price
,us_currency
,jp_name
,jp_price
,jp_currency
. - To prevent storing duplicated items in the database, we implement uniqueness validations, ie:
item
uniqueness bysku
,store
uniqueness byaddress
,category
uniquenes byname
- We flatten all item attributes into a flat
items
table (see: Rails Backend Service point no. 2).- Pros:
- It simplfies our API schema and prevents sending and receiving complex nested attributes between frontend and backend services.
- It helps to speed up our database query by avoiding complex queries using joint tables.
- Cons:
- As we open more countries, we'll need to add more columns by a factor of 3 (ie.
name
,price
, andcurrency
). - Adding columns will break our API and complicate the deployment.
- As we open more countries, we'll need to add more columns by a factor of 3 (ie.
- Risk mitigation #1: improve Database Schema to allow more complex queries:
- follow the same schema from
githubusercontent
endpoints. - create separate tables for
items
,categories
,stores
, and join tablecategory_items
items
has_onecategory
category
has_manyitems
stores
has_manyitems
andcategories
- follow the same schema from
- Risk mitigation #2:
- We can take advantage React Context to set multi-language translations using json files, instead of coding translation logics.
- If using Rails view, we can take advantage of Rails
I18n
to manage static and activerecords translations andGlobalize
gem to create table duplicates to handle items' localizedname
andprice
.
- Pros:
- Using React lifecycle management to fetch and populate data from
githubusercontent
andRails database
.- Pros:
- By using
componentDidMount()
, we'll be able to get the data before the main component renders and ensure users will not see null data. - It automates fetching data without requiring Users to run a function.
- By using
- Cons:
- The app will fetch data everytime the component is rendered.
- If the service fails, the app will not render at all.
- Risk mitigation:
- take advantage
Redux Middleware
to handle API calls and use data inRedux store
to render data on the frontend app. - use
cron jobs
backend service to fetch data from outside server.
- take advantage
- Pros:
-
Security between 2 stand-alone frontend and backend apps
rack-cors
configuration needs to include restricting the original URL and methods.- use
token
to authenticate communication between both apps. - implement User authentication, eg. Rails
devise
gem.
-
Validations
- [frontend] adds form validations to minimize human errors when changing prices.
- [backend] utilizes Rails ActiveRecord validations to ensure database validity.
-
Testings
- [frontend] uses
jasmine
andenzyme
to test our React components/ - [backend] runs
model
andintegration
tests to ensure correct data associations and HTTP response from the frontend app.
- [frontend] uses
-
Cron Job
- Current implementation relies on frontend app to fetch, transform, and populate data. It creates two potential issues:
- unreliable services make create data discrepancy between our data source and backend database.
- requires users to run and monitor this functionality.
- Fetching data from the source should be delegated as backend cron jobs. We can utilize Rails
sidekiq
andredis
to sync data properly even when there's service disruption.
- Current implementation relies on frontend app to fetch, transform, and populate data. It creates two potential issues:
-
Deployment
- We can deploy one hosting solution to run
frontend
,backend
, anddatabase
services or have separate hosting solution for each service.
- We can deploy one hosting solution to run