This project demonstrates a minimal event-driven backend architecture on AWS using serverless components. The entire stack runs locally using LocalStack, allowing the system to be deployed and tested without accessing real AWS account.
A short walkthrough of the system architecture and demo flow:
Commands are handled by Lambda functions which generate domain events. These events are stored in DynamoDB and distributed through EventBridge to projections and asynchronous workers.
flowchart LR
Client[Client Apps]
APIGateway[API Gateway]
subgraph Commands
AuthLambda[Auth Lambda]
ObservationCommand[Observation Command Lambda]
end
subgraph Queries
QueryAlerts[Query Alerts Lambda]
end
EventStore[(DynamoDB Events Table)]
EventBridge[(EventBridge Bus)]
subgraph Projections
PatientProjection[Patient Projection Lambda]
ObservationProjection[Observation Projection Lambda]
AlertProjection[Alert Projection Lambda]
end
SQSQueue[(SQS Queue)]
AlertWorker[Alert Worker Lambda]
subgraph ReadModels
Patients[(Patients Table)]
Observations[(Observations Table)]
Alerts[(Alerts Table)]
end
Client --> APIGateway
APIGateway --> AuthLambda
APIGateway --> ObservationCommand
APIGateway --> QueryAlerts
ObservationCommand --> EventStore
ObservationCommand --> EventBridge
AuthLambda --> EventStore
AuthLambda --> EventBridge
EventBridge --> PatientProjection
EventBridge --> ObservationProjection
EventBridge --> SQSQueue
EventBridge --> AlertProjection
SQSQueue --> AlertWorker
AlertWorker --> EventStore
AlertWorker --> EventBridge
PatientProjection --> Patients
ObservationProjection --> Observations
AlertProjection --> Alerts
QueryAlerts --> Alerts
QueryAlerts --> Patients
healthflow-aws
│
├── infra
│ ├── bin
│ │ └── healthflow.ts
│ └── lib
│ └── healthflow-stack.ts
│
├── services
│ ├── auth
│ ├── alert_worker
│ ├── authorizer
│ ├── observation_command
│ ├── projection_alert
│ ├── projection_patient
│ ├── projection_observation
│ ├── query_alerts
│ └── shared
│
├── frontend
│ ├── patient
│ └── clinician
│
└── tools
Infrastructure is defined using AWS CDK with TypeScript in infra/lib/healthflow-stack.ts.
The CDK stack creates:
- API Gateway
- Lambda functions
- EventBridge bus
- SQS queue
- DynamoDB tables
Example scenario: a patient submits an observation.
- Patient sends
POST /observations - API Gateway invokes
ObservationCommandLambda - Lambda creates an ObservationSubmitted event
- Event is stored in DynamoDB event store
- Event is published to EventBridge
- EventBridge distributes the event to:
- Observation projection
- SQS queue
- Alert worker processes the observation asynchronously
- If a threshold is exceeded, an AlertCreated event is generated
- Alert projection updates the alerts read model
- Clinician retrieves alerts via
GET /alerts
Public HTTP entry point for the system. API Gateway routes requests to Lambda functions.
Endpoints:
POST /login
POST /observations
GET /alerts
Command and query responsibilities are separated. Commands generate domain events which are stored in the DynamoDB event store and published to EventBridge. Events stored in DynamoDB form the immutable event log of the system. All projections and read models are derived from this event stream and can be rebuilt by replaying stored events.
POST /observations
-> ObservationCommand Lambda
-> ObservationSubmitted event
-> stored in events DynamoDB table
-> published to EventBridge
Projections build read models used by queries:
GET /alerts
-> QueryAlerts Lambda
-> alerts read model from DynamoDB
Each service is implemented as a small Python Lambda (./services/).
Each Lambda is focused on a single responsibility.
| Lambda | Purpose |
|---|---|
| auth | user login and patient creation |
| observation_command | handles observation submission |
| alert_worker | evaluates observations asynchronously |
| projection_patient | builds patient read model |
| projection_observation | builds observation read model |
| projection_alert | builds alert read model |
| query_alerts | returns alerts for clinicians |
The services/shared module contains reusable helpers used by multiple Lambdas.
| Module | Purpose |
|---|---|
| aws | boto3 helpers and LocalStack support |
| auth | JWT token creation and validation |
| events | event building and publishing |
| request | API Gateway request parsing |
| response | API Gateway response formatting |
| config | environment configuration |
Tables used in the system:
healthflow-events
healthflow-users
healthflow-patients
healthflow-observations
healthflow-alerts
Roles:
- events table - event store
- users table - authentication user data
- patients / observations / alerts - read models
Central event bus used for distributing domain events.
Example events:
PatientCreated
ObservationSubmitted
AlertCreated
SQS is used for asynchronous processing. This allows long-running logic to be processed outside of the request path.
Example flow:
ObservationSubmitted -> EventBridge -> SQS -> Alert Worker
- Python 3.11+
- Docker
- Node.js + npm
- AWS CLI
- awscli-local (
awslocal) - LocalStack CLI (
localstack,cdklocal)
LocalStack runs AWS services locally using Docker containers.
The helper tools awslocal and cdklocal are used by the project scripts in ./tools/.
The project includes helper scripts in ./tools/ that start the entire local environment. deploy.sh script performs the following steps:
- Reset LocalStack containers
- Start LocalStack with Docker
- Deploy AWS infrastructure using CDK
- Seed demo user data (
clinician) - Start the frontend applications
Run:
./tools/deploy.sh| Service | URL |
|---|---|
| Patient UI | http://localhost:5173 |
| Clinician UI | http://localhost:5174 |
| LocalStack Dashboard | https://app.localstack.cloud |
A demo clinician user is seeded automatically.
username: clinician1
password: demo
