I bootstrapped a yarn workspace
monorepo. The structure is pretty simple:
|- **apps/**: top level apps: Website, LiveMarket, AdminPortal, etc.
| |- **webapp/**
|- **features/**: main entry for a feature and its pages, usually tied to app frameworks
| |- **livemarket/**: feature for this exercise
|- **devtools/**: development tools (cypress, storybook, jest configs etc)
| |- **storybook/**
| |- **cypress/**
|- **packages/**: app agnostic, independently versionable packages
| |- **api/**: data providers
| |- **ui**: base ui
| |- **components/**: shared components
| |- **utils/**: shared utilities and helpers
The idea here is that you can have multiple consuming apps (web and mobile), these should be as leightweight as possible and delegate routes to a feature. Features compose multiple components making use of apis and utils to form functionality.
NOTE: Opted not to use nx, it is a great tool but it has it shortcomings, this is a great follow up topic to discuss.
I'll be tackling this challenge in a bottom up approach, making use of storybook I will create an initial set of components for the live market using mocked data. I will then compose these into a page and hook them up to use data fetched from an api serving the provided json files. To open storybook run yarn storybook
We try to keep the apps as lightweight as possible, usually only handling mapping routing to appropriate features.
Features will then ofcourse be heavily coupled to the apps, and will make use of their dependencies as peerDependencies. In this example each feature would have Container
type entry points which handle the serer side data fetching and feed the data into a presentational component
which then enhances itself client side with hooks.
Data fetching is abstracted into a promised based api/
package and can be used both server and client side. I have included a different fetch per type of order to be displayed, then a shorthand for fetching all orders in parallel async resolving via Promise.all
, this is more performant than doing them individually.
We then have our features/livemarket
package which has a LiveMarketPageContainer
to fetch all orders server side and feed them to our presentational LiveMarketPage
which makes use of a custom userOrders
hook to update the ui accordingly.
For accepting
and rejecting
orders we have event handlers in our custom hook which currently have a placeholder for where we would call the backend to update order status, and it does an optimistic update on the ui to reflect the action, assuming the backend will eventually sync. (NOTE: the backend is missing so reloading the page will show original data - we could persist state in session
if we wanted to be extra optimistic, but that's for another challenge...)
The page is composed using the CollapsibleSection
and OrdersTable
components which are purely presentational and app agnostic. These are expected to work on any JSX based app.
I usually opt to use Chakra
or MaterialUI
for web based apps, and NativeBase
for mobile based apps, as this take the whole css layer away from the equation and allow for prop driven design system components. For this challenged I am using tailwind
, I prefer to keep the JSX components clean and agnostic
so use css modules
and apply tilwind styling through those. NOTE: this will then require a build config to make the styles atomic
and remove duplication for a slimmer css file.
Code confidence is THE most important thing for releasing software, so it is important to cover your back from all angles! For this challenge we include:
yarn lint
yarn typecheck
yarn test
yarn cy:storybook
and you can view regression snapshots in devtools/cypress/snapshots/storybook
yarn cy
and you can view snapshots in devtools/cypress/snapshots
All the above checks should run on PR and block merge if any issues.
In a production ready setup we would also add e2e tests against a deployed instance to run on master alognside the deployment tasks.
Build and run with:
docker build --tag pocApp .
docker run -p 3000:3000 -e API_HOST="http://host.docker.internal:3000" pocApp