Corporate Wellness Tracking Platform

Overview

A multi-tenant platform enabling companies to engage employees through gamified health challenges, digital health assessments, activity tracking via wearables, a points-based reward economy, and a partner benefit marketplace – serving both web and mobile clients, with full English/French localization.

DomainB2B Corporate Wellness & Health
RoleTech Lead – System Architect, design, and full-stack delivery
CloudAWS (Lambda, API Gateway, SQS, SSM, Amplify, VPC)
StackTypeScript, Node.js, React, MongoDB, Redis
Scale14 microservices, ~68 API endpoints, 33 data models, 5 external integrations

Architecture


Technical Decisions

The decisions below are the ones I consider most consequential. Each shaped how the system behaves at scale and under failure.

Serverless in a VPC

DecisionWhy
AWS Lambda over containersZero-ops scaling for bursty B2B traffic patterns; pay-per-invocation cost model aligned with multi-tenant usage
All Lambdas in VPC private subnetsEnterprise compliance requirement – no direct internet exposure; outbound routed through NAT Gateway with static IP for third-party whitelisting
SQS FIFO for financial transactionsExactly-once, ordered processing for the points economy; decoupled write path survives Lambda cold starts

Monorepo with Shared Packages

DecisionWhy
Lerna + Yarn Workspaces15 shared libraries + 14 service modules in one repo; code reuse without sacrificing independent deployability
Shared packages for cross-cutting concernsDatabase layer (33 models), authorization (RBAC), token management, request validation – each service imports what it needs without duplicating logic
Per-service Serverless configEach microservice deploys independently with its own IAM role, VPC binding, and environment; no blast radius across services

Security by Design

DecisionWhy
Auth0 for identityDelegated authentication for both end-users and admins; multi-tenant with separate database connections per client
Casbin RBAC with dynamic policiesOperators get static role-based policies; end-users get dynamically generated policies scoped to their own resources – no permissions database to maintain
Encrypted PII collectionSeparate encrypted data model for personal information; application-level encryption on top of at-rest encryption

System Design Deep Dives

1. Points Economy – Async Transaction Processing

The most architecturally interesting subsystem. Users earn points through various actions (completing challenges, tracking activity, reading articles). The system must guarantee no double-rewards and no balance inconsistency.

Design rationale:

  • Two-phase via queues rather than a single synchronous write – if the balance update fails, the transaction stays PENDING and can be retried without re-issuing the reward
  • FIFO with deduplication prevents double-rewards from retries or duplicate API calls
  • Decoupled workers mean the user gets a fast 202 response; the heavy work happens asynchronously

2. Authorization – Dynamic Policy Generation

Rather than maintaining a permissions database, I designed the authorization layer to generate access policies on the fly from the JWT token:

Why this approach: Adding a new user requires zero permission configuration. Their access scope is derived entirely from their identity token. Operator roles are defined declaratively as policy rules – adding a new admin role is a config change, not a code change.

3. Challenge Engine – Unified Model, Multiple Lifecycles

Three distinct challenge types (individual, team, company-wide collaborative) sharing a single data model but with type-specific state machines:

The design tradeoff: A single unified model means simpler queries and shared progression logic, but requires careful validation to enforce type-specific rules. I extracted this into a dedicated shared package so every service that touches challenges uses the same state machine.


Request Handler Pattern

Every endpoint across all 14 services follows the same pipeline. This was a deliberate architectural choice to enforce consistency:

Why it matters: New services or endpoints added by any team member follow the same auth, validation, and error-handling path. No one can accidentally skip authorization. The shared response formatter guarantees consistent CORS headers and status codes.


Key Engineering Challenges

ProblemApproachResult
Financial consistency in a distributed points systemTwo-phase async processing with SQS FIFO; PENDING → RESOLVED transaction statesZero balance inconsistencies; fast user-facing response
Multi-tenant authorization without a permissions DBDynamic Casbin policies generated from identity tokensInstant policy changes; zero per-user configuration
Cold-start latency in VPC LambdasMongoDB connection pooling; provisioned concurrency for critical pathsMet enterprise security requirements without sacrificing UX
Three challenge types, one codebaseUnified data model with type-specific state machines in a shared packageConsistent API surface; new challenge types require only new state rules
15 shared packages across 14 servicesLerna monorepo with Yarn Workspaces; clear package boundariesCode reuse without deployment coupling
5 external enterprise integrationsDedicated integration package per vendor with standardized error handlingClean abstraction; swapping a vendor touches one package

Technology Stack

AreaTechnologies
BackendTypeScript, Node.js, Serverless Framework, MongoDB (Mongoose), Redis, Yup, Jest
FrontendTypeScript, React, Redux Toolkit, React Query, Material-UI, Formik, i18next
CloudAWS Lambda (VPC), API Gateway, SQS FIFO, SSM, Amplify, NAT Gateway
AuthAuth0 (identity), Casbin (RBAC), JWT, application-level encryption
IntegrationsSalesforce (CRM), Validic (health data), WordPress (CMS), Auth0 (IdP)
QualitySonarQube, ESLint, OpenAPI 3.0 documentation

Retrospective – What I Would Do Differently

AreaWhat wasWhat I’d changeWhy
ObservabilityBasic loggingDistributed tracing (X-Ray) + structured logging + dashboards14 services across queues – debugging without traces is painful
Transaction integrityApp-level two-phase via SQSMongoDB multi-document transactions for the synchronous pathReduces complexity for cases that don’t need async
Scheduled jobsManual deployment (cron workaround)EventBridge SchedulerThe Serverless Framework cron bug was worked around rather than solved
Frontend buildCRA (Create React App)Vite or Next.jsFaster builds, better DX, SSR options for SEO-relevant pages
Secret managementSSM (with some legacy hardcoded dev values)SSM-only + pre-commit secret scanning (e.g., gitleaks)Defense in depth; no secrets should ever touch source control