Genomas Health Platform
A Turborepo monorepo serving three healthcare products from one typed design system.
Role
Senior Frontend Engineer — frontend architecture & delivery lead
Engagement
Full-time at Segwitz
Client
Genomas
Period
2023 — Present
The problem
Three healthcare products had grown from the same origin into three codebases, each with its own component library, its own fetch layer and its own idea of what a button does. Features shipped to one product had to be re-implemented in the others, healthcare-specific states (loading, empty, error, restricted-by-role) were inconsistent, and clinical reviewers were seeing different behaviour for the same data on different surfaces. Release risk was climbing faster than the roadmap.
My responsibility
Frontend architecture and delivery lead across all three surfaces. I defined the monorepo structure and package boundaries, set the data-fetching and state conventions, built the shared component and type layer, reviewed every significant PR, and worked directly with product and clinical stakeholders to turn ambiguous healthcare requirements into typed interfaces.
Surfaces shipped
Novameds B2B
Wholesale ordering surface for clinics and distributors: catalogues, negotiated pricing tiers, multi-cart checkout and order history.
Reveal B2C
Consumer-facing genetic testing storefront with kit selection, a guided sample-registration flow and result delivery.
Admin Portal
Internal operations console — patient and sample records, lab pipeline states, user roles, content and reporting.
Architecture
How it was structured, and the reasoning behind each boundary.
- 01
Turborepo monorepo
Three apps — novameds, reveal, admin — plus shared packages for ui, hooks, api-client, types and config. Task graph with remote caching means a change to a shared component rebuilds only what depends on it, and CI stays under control as the repo grows.
- 02
Server state with React Query
One query-key factory per domain, explicit stale times, and an invalidation map co-located with mutations. Optimistic updates only where the backend contract guarantees them. Prefetching on intent for the flows where perceived latency mattered most.
- 03
UI state with Zustand
Cart, filters, wizard progress and panel layout live in small Zustand slices with selectors, kept entirely out of the server cache. The rule enforced in review: if the server owns it, React Query owns it.
- 04
Typed contracts end to end
API response types generated from backend schemas and consumed by all three apps, so a field rename is a compile error rather than a runtime surprise in a clinical workflow.
- 05
Role-aware rendering
A single permission model drives navigation, row-level actions and route guards across Novameds, Reveal and Admin — implemented once in the shared layer instead of three times with drift.
Key challenges
- 01
Merging three design languages without stalling delivery
A big-bang rewrite was off the table — features kept shipping. I extracted the shared UI package incrementally, starting with the highest-usage primitives, and codemodded consumers app by app so each migration was independently shippable and revertible.
- 02
Healthcare-grade data handling
Clinical records mean PII, strict role boundaries and audit trails. Every list, export and mutation needed explicit permission checks, defensive empty states and no client-side caching of sensitive payloads beyond what the flow required.
- 03
Cache coherence across tabs and roles
An admin action can invalidate data a B2B user is looking at. Invalidation was modelled per domain rather than per screen, with targeted query invalidation instead of blanket refetches that would have hammered the API.
- 04
Long, stateful multi-step flows
Sample registration and wholesale checkout both span several steps with validation and resumability. A shared wizard primitive handles persistence, step validation and deep-linkable state so each product only writes its business rules.
Implementation
- 01
Defined the boundaries first
Before moving code, I documented what belongs in each package and why — including what deliberately stays app-specific. That document is the reason the monorepo hasn't decayed into a shared-everything ball of mud.
- 02
Shared UI with documented states
Components ship with Storybook stories for default, hover, focus, loading, empty, error and disabled. Healthcare flows live or die on the error state, so those were written first, not last.
- 03
Progressive migration
Legacy screens were wrapped rather than rewritten where a rewrite carried no user-visible benefit, buying time to spend on the flows that did matter.
- 04
Performance guardrails in CI
Bundle size budgets, lint rules against barrel-file imports in shared packages, and build time tracked per app so regressions surface in the PR that caused them.
Results
3
Products on one codebase
1
Shared design system
B2B · B2C · Admin
Surfaces covered
- 01Three products now ship from one design system and one type layer — a component built once appears on all three surfaces.
- 02Duplicated fetch and caching bugs dropped sharply once server state and UI state had enforced homes.
- 03Onboarding a new frontend engineer takes days rather than weeks: one repo, one convention doc, one component catalogue.
- 04CI time stayed flat through the migration thanks to Turborepo caching, even as the number of apps grew.
- 05Role-based access and audit behaviour are now consistent across B2B, B2C and internal admin.
Technologies
- Next.js
- React
- TypeScript
- Turborepo
- React Query
- Zustand
- Tailwind CSS
- Storybook
- Jest
- Cypress
- GraphQL
- Docker
Technical highlights
- Turborepo app/package topology designed for three diverging products
- Query-key factory + invalidation map as a team convention
- Shared wizard primitive with resumable, deep-linkable multi-step state
- Incremental design-system extraction with zero delivery pause
Building something similar? I’d like to hear about it.
Start a conversation