Skip to content

Code architecture and design decisions

Stefan Piatek edited this page Nov 16, 2022 · 9 revisions

This page will first describe the overall architecture of the React application. We will then explain our design decisions for some of the core features of the app.

Architecture

Code entrypoint

All custom react typescript code exists within the /src directory and the entry point for the application is index.tsx where our main App component is rendered.

App.tsx declares the main routing for the app. This tells the application how and when to render the top-level pages and components. Inspecting the imports declared in App.tsx will reveal the overall project structure, components, and sub-components.


Core project folder structure

This codebase follows a typical folder structure for a React project. A main pages folder contains the core files that represent pages you can navigate to in the running app. These main pages are built from individual React component files in the components folder. A number of other utility files and folders offer various bits of functionality in support of the main pages and components.

src
├── code_systems
├── components
├── fhir
├── pages
└── utils
Component Purpose
code_systems Functions to interact with interoperability coding systems, and statically defined sets of codes that can be used
components Contains React components that are the individual building blocks that form the main application
fhir Functions that interact with the FHIR API resources and endpoints
pages Main React parent components that represent pages you can navigate to in the running app
utils Utility files that offer other functionality

Pages

Pages you can navigate to in the running app exist as React files within the /pages folder. Within each page file you will find the entrypoint for that page rendered as a React component. Inspecting these page components will reveal a number of subcomponents that make up the overall structure and logic of each page.

The core pages that make up the application:

src
├── code_systems
├── components
├── fhir
├── pages
│   ├── NewReport.tsx
│   └── Results.tsx
└── utils
Page Purpose
NewReport.tsx Renders the main form component where users enter patient data and submit to the fhir api
Results.tsx A table that suggests the necessity for cascade testing

Components

Components are what make up the individual building blocks of each core page. They tell each page what content to render (the structure) and how to render it (the logic).

Each core component folder contains a number of individual component files. This keeps each file lean and gives each one a specific purpose.

src
├── code_systems
├── components
│   ├── fhir
│   ├── layout
│   ├── reports
│   ├── results-list
│   └── UI
├── fhir
├── pages
└── utils
Component folder Purpose
fhir Contains a fhir Authoriser and React context for providing auth status across the entire application
layout Provides a consistent general layout and set of styles that persist across the entire application
reports Components for the main form entry page where users input patient data and submit to the fhir api
results-list Components for the main table page that suggests the necessity for cascade testing
UI General UI components (buttons, modals, drop-downs etc.)

Design decisions

This section documents a number of decisions that were made when designing and architecting the application.

FHIR dev server

This project uses a local development implementation of a FHIR server to allow us to run queries during development work. We use a dockerised version of the open source FHIR server project HAPI-FHIR. This provides a full development FHIR server that runs locally on your machine for testing purposes. Instructions for how to spin up the development server can be found in the main project repo README.md.

FHIR auth flow

In order to interact with the FHIR server it is necessary that you are authenticated. Authentication is provided here via a JavaScript client for FHIR: fhirclient. This client exposes methods for both authentication and making queries against the FHIR server, for example fetching patient data.

Our web app uses this auth flow example as inspiration for authenticating users and exposing the FHIR client instance to the app via React Context. The example has been slightly altered for our implementation, but the concept is still the same.

You will notice that our app authenticates users automatically when they navigate to the root / of the app. This is convenient for development purposes, but in a production app users would be navigated to a separate login page and redirected back to the app when authentication has been completed.

Form building and data validation

One of the primary aspects of this application is to allow clinicians to enter data into a form and submit that data via the FHIR server api. As such, the app makes heavy use of forms and form data validation. We use the Formik library for building the form itself which integrates nicely with React. We also use Yup which provides robust schema validation for data entered into the form.

The use of Formik and Yup has two primary benefits:

  1. Better user experience (i.e. form feedback on data entry errors etc.)
  2. Robust data validation prevents incorrect data being submitted to FHIR

Non-optimal solutions

Given limited time and the prototype nature of the app, a few design decisions were implemented that may not be completely optimal. You may wish to review these features for possible future improvements.

Hardcoded variables

The form currently allows you to select a lab from a drop-down list in order to pre-populate some form fields. The list of labs is currently hardcoded into the application. A better solution may be to dynamically fetch this information using an appropriate source.

Dynamically querying genes

In the Variant stage of the form, the user can enter data into the Search for genes symbols field to make a dynamic search based on the user input. The results of the input then allow the user to select an appropriate Gene Symbol in the next form field drop-down list.

For now, entering data into the search form field fires a network request on every keystroke. Future implementations may wish to use debouncing to make api network requests less frequent.