Nextcare
Nextcare is a national healthcare provider search tool built with Next.js and Now.
Overview
- Quick Start
- Available scripts
- Build process and workflow
- Architecture
- Frontend structure
- Backend structure
- Database structure
- Analysis
- UI/UX Design
- Conclusion
Quick Start
Installation
npm i -g now
npm i
Available scripts
npm run dev
To start dev servers for your application and API.
The application will be available at http://localhost:3000/
and your api server will be be listening port 3001
under localhost
.
npm run lint
To lint the codebase under /www
and /api
.
If you'd like to watch your files, run npm run lint:watch
to execute Eslint Watch
. Linting over only your staged files are available too with npm run lint-staged
.
npm run test
To run unit tests.
The script will execute Jest to run against your tests and generate coverage report in the application root. If you'd like to run Jest's watch mode, simply run npm run test:watch
.
npm run cz
To use Commitizen to generate commit messages.
Using Commitizen to commit your code in the project is important for the release process. In the release process, we will use the commit messages to generate or update CHANGELOG.md
and git tag
.
npm run release
To generate CHANGELOG.md
and tag your commit for release with Standard Version.
Standard version will bump your release version according to your commit messages. In order for Standard version to parse your our commit messages properly, the message should follow the Conventional Commits.
Build process and workflow
The workflow is following Trunk Based Development. Each branch is a trunk and once the the branch is merged to master and pushed, Zeit Now will start the deploy process.
To start developing, Please run npm run dev
. The project has integrated with a couple of git hooks with Husky.
- Pre-commit: Husky will run linter over your staged files.
- Pre-push: Husky will run linter and unit test against the project to make sure there is no linting errors and pass all the tests.
Once the branch is ready to merge, please run npm run release
to update CHANGELOG.me
and generate tag with a version bump. Standard version will commit the change for us when it finishes the process. Execute git push --follow-tags <remote> <branch>
to push the branch with tags.
The project is using Prettier and Eslint to lint the code on the fly. Please make sure you integrate them in your editor. Please turn on formatOnSave
for the optimal developer experience.
Architecture
We embrace Cloud computing solutions for the scalability. The project with build and deployed with Serverless architecture. Each page in the application and API endpoint is a standalone lambda function. This approach gives us the flexibility of Micro-services and empowers FaaS.
We have 4 Lambdas that are being deployed on Zeit Now.
- Index page -
/www/pages/index
: Login with Google Sign-In - Search page -
/www/pages/search
: provide searching and filtering functionalities the the users to look for healthcare providers. - Authorization API -
/api/auth
: process signin information form Google Sign-in and response with user information with access token. - Search API -
/api/provider
: process filter criteria and response with found record from Database.
We adapt NoSQL database for this application. MongoDB offers scalability and flexibility to query and optimize over data. Its data structure in JSON-like format creates harmony in data throughout the application that increase the agility and speed of the development cycle and developer experience.
We deploy the database on mLab, which is a distributed cloud Database-as-a-Service for MongoDB. It complements well with serverless architecture and it offer great tooling around backup, security, and analytics.
Frontend structure
The project adapt server side rendering for performance optimization. Client side routing and server side routing are supported in page navigation. This way, we can benefit from SSR in performance boost and still have seamless navigation for better user experience.
Technology
- React: a component based UI library to effectively render declarative views. We are using React Hooks for code quality and performance optimization.
- Redux: a functional state management library that allow us to seperate application state from components to create a clean structure.
- Redux Thunk: Thunks handles side effects to keep components as pure functions and asynchronously update Redux store.
- Material UI: a comprehensive UI library with great design system that aims for optimal user experience
- React Testing Library: a light-weight yet strong testing library for React.
- Reselect: an optimal selector library to select state properties from redux store. Note: It has been removed from the project dependency because of lack of use case with the current business requirement. However, it is worth mentioning since it plays a key part of the frontend structure.
UI Flow
To have a closer look of how each part work together, please take a look at the following diagram.
When an Event is triggered in a component, the store dispatches an Action that carries data in the payload in its callback function. An action persists a structure as following
{ type: String, payload: Any }
By dispatching an action, the Reducer in the store knows how to use the payload and update its state. If the action is an async action, the Thunk will perform the tasks (HTTP requests, logging, etc.) and dispatch simple actions along the way until the thunk is completed.
The component then will consume the state in the store by using selectors to select the state properties it requires to render. Once the component re-computes and being rendered with the updated state, the flow completes.
Backend structure
Technology
- Micro: a asynchronous HTTP microservice library to handle requests and responses
- Micro Router: a router that handles routes of APIs.
- Mongoose: an ORM for MongoDB. It offers rich toolsets to write functional interactions with database.
Our backend are composed with Lambda functions in a Serverless, Function-as-a-Service cloud architecture. Each Lambda is a function with a signature as following
function(req: Object, res: Object): void
There are two APIs
/api/auth
POST To sign in user with Google Sign-In information.
It will find the user based on google Id and update access token in the database. If it is a new user, a new user in database will be created.
Note: We are reusing google's access token as the applications access token for the purpose of demonstration.
Request
{
body: {
accessToken: String,
email: String,
familyName: String,
givenName: String,
googleId: String,
imageUrl: String,
name: String
}
}
Response
Code 200: Ok
{
body: {
accessToken: String,
email: String,
familyName: String,
givenName: String,
googleId: String,
imageUrl: String,
name: String
}
}
Code 500: Internal Error
{
body: {
{ error: { details: Error }
}
}
/api/providers
GET Returns healthcare provider records.
A authorization Bearer token has to be included in the header
Request
{
headers: {
authorization: String,
}
query: String
}
Query parameters
{
state?: String,
min_discharges?: String,
max_discharges?: String,
min_average_covered_charges?: String,
max_average_covered_charges?: String,
min_average_medicare_payments?: String,
max_average_medicare_payments?: String,
return_fields?: String,
}
Response
Code 200: Ok
{
body: {
data: {
averageCoveredCharges: Number;
averageMedicarePayments: Number;
averageTotalPayments: Number;
drgDefinition: String;
hospitalReferralRegionDescription: String;
providerCity: String;
providerId: String;
providerName: String;
providerState: String;
providerStreetAddress: String;
providerZipCode: Number;
totalDischarges: Number;
_id: String;
}
}
}
Code 401: Unauthorized
{
body: {
{ error: { details: String }
}
}
Code 500: Internal Error
{
body: {
{ error: { details: Error }
}
}
Database structure
We have two collections in database: users and providers. They is indexed in the combaniations of Single Field Indexes and Compound Indexes to speed up the query performance.
User
Schema
{
email: String,
familyName: String,
givenName: String,
googleId: String,
imageUrl: String,
name: String,
accessToken: String,
}
Indexes
{"googleId": 1}
{"accessToken": 1}
Provider
Schema
{
drgDefinition: String,
providerId: Number,
providerName: String,
providerStreetAddress: String,
providerCity: String,
providerState: String,
providerZipCode: Number,
hospitalReferralRegionDescription: String,
totalDischarges: Number,
averageCoveredCharges: Number,
averageTotalPayments: Number,
averageMedicarePayments: Number,
}
Indexes
{
"providerState": 1,
"totalDischarges"?: 1,
"averageCoveredCharges"?: 1,
"averageMedicarePayments"?: 1
}
Analysis
The test performance Synthetic monitoring is done by Google Lighthouse.
With the current build, the performance metrics are listed bellow
Mobile with Simulated Fast 3G, 4x CPU Slowdown throttling
- First Contentful Paint: 1.6s
- First Meaningful Paint: 2.3s
- Speed Index: 3.4s
- First CPU Idle: 3.2s
- Time to Interactive: 3.2s
- Estimated Input Latency: 140ms
Desktop with no throttling
- First Contentful Paint: 0.6s
- First Meaningful Paint: 1.6s
- Speed Index: 1.0s
- First CPU Idle: 1.6s
- Time to Interactive: 1.6s
- Estimated Input Latency: 20ms
UI/UX Design
The Project follows Mobile-first and responsive design principle and adapt the content of the pages responsively according to viewport size.
The design is evaluated with Usability Heuristics to cover User experience evaluation.
Please find the following screenshots for the responsive design demonstration.
Phone
Tablet
Desktop
Future Improvement
- Login page showing sign in result from Google Sign-In. Currently it does not reflect whether signin is successful or failed.
- Show Desktop filter when scrolling down to make it more accessible to the users.
- Use Windowing technique to render large number of list items
Conclusion
The project has put
- architecture
- performance
- development process
- release pipeline
- user experience
- scalability
in mind to deliver optimal quality product with short Time to Market. The technology stack is flexible and proven effective to deliver product for individuals or for teams while focus on delivering great User Experience to the customers.