Skip to content

Latest commit

 

History

History
125 lines (109 loc) · 5.72 KB

File metadata and controls

125 lines (109 loc) · 5.72 KB

Patient Management Sample Apps

This repository hosts six small, production-grade sample applications that all implement the same simple Patient Management tool. Each sample demonstrates vertical-slice architecture and testable code while using SQLite for persistence and a single OAuth provider for authentication.

Goals

  • Showcase identical business capabilities implemented across different stacks
  • Emphasize vertical slices (feature-first structure) and clear testing boundaries
  • Keep the domain intentionally small but realistic

Core User Stories (identical across all samples)

  • As a logged-in user, I can create patients
  • Patients have the following properties:
    • id: a 7-digit number starting with a non-zero digit (regex: ^[1-9][0-9]{6}$)
    • name: string, maximum 50 characters
  • A logged-in user can search for patients by id or name
  • A logged-in user may edit a patient’s name
  • A logged-in user may archive a patient
  • A logged-in user may toggle whether archived patients are included in search
  • The search should indicate if an archived patient matches the query, but must not return the archived patient in the list when the toggle is off
  • A logged-in user can add a note to a patient
  • A logged-in user can see who created a note and when
  • A logged-in user can edit and delete notes
  • Deleted notes cannot be restored
  • Notes have audit histories; history stores previous text
  • History cannot be edited, but is deleted with the note
  • A logged-in user can search a patient’s notes

Shared Technical Requirements

  • Database: SQLite for all samples
  • Authentication: Single OAuth provider across all samples
    • Recommended: Auth0 (broad language support, good quickstarts)
    • Alternatives: Okta, Azure AD B2C, or GitHub OAuth for simple dev/testing
    • Minimum claims: stable user identifier (e.g., sub) used to attribute created/updated-by fields
  • Validation:
    • Patient id must match ^[1-9][0-9]{6}$
    • Patient name required, <= 50 characters
    • Notes text required and non-empty
  • Archival behavior:
    • Archived patients are excluded by default
    • When excluded, the search should display a message like “1 archived match hidden” instead of listing the patient
    • When included, archived patients appear and are clearly marked

Domain Model (shared intent)

  • Patients
    • patient_id (TEXT or INTEGER), name (TEXT), archived (BOOLEAN/INTEGER), timestamps
  • Notes
    • note_id, patient_id (FK), text, created_by_user_id, created_at, updated_at, soft delete flag or hard delete per stack implementation
  • Note History
    • note_history_id, note_id (FK), previous_text, changed_at, changed_by_user_id

SQLite reference schema (illustrative; each stack may implement via its own migration tooling):

-- Patients
CREATE TABLE patients (
  patient_id TEXT PRIMARY KEY CHECK (patient_id GLOB '[1-9][0-9][0-9][0-9][0-9][0-9][0-9]'),
  name TEXT NOT NULL CHECK (length(name) <= 50),
  archived INTEGER NOT NULL DEFAULT 0,
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX idx_patients_name ON patients(name);

-- Notes
CREATE TABLE notes (
  note_id INTEGER PRIMARY KEY AUTOINCREMENT,
  patient_id TEXT NOT NULL REFERENCES patients(patient_id) ON DELETE CASCADE,
  text TEXT NOT NULL,
  created_by_user_id TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at TEXT NOT NULL DEFAULT (datetime('now')),
  deleted INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX idx_notes_patient ON notes(patient_id);
CREATE INDEX idx_notes_text ON notes(text);

-- Note History (append-only; removed when note is hard-deleted)
CREATE TABLE note_history (
  note_history_id INTEGER PRIMARY KEY AUTOINCREMENT,
  note_id INTEGER NOT NULL REFERENCES notes(note_id) ON DELETE CASCADE,
  previous_text TEXT NOT NULL,
  changed_at TEXT NOT NULL DEFAULT (datetime('now')),
  changed_by_user_id TEXT NOT NULL
);

Search Behavior (consistent UX)

  • Query by patient_id or partial name
  • Toggle: “Include archived” (default: off)
  • When toggle is off and matches exist only among archived patients, show a non-intrusive notice like: “N archived matches hidden”
  • Notes search is scoped to a specific patient

Vertical Slices and Testability

Each sample organizes code by feature (vertical slices) rather than layers:

  • Feature folders: patients, notes, auth
  • Each feature includes its own handlers/controllers, validation, data access, and tests
  • End-to-end tests cover critical flows: create/search/edit/archive patients; add/edit/delete notes; history behavior; archived toggle behavior

Planned Samples (folders in repo root)

  • .net + razor pagesdotnet-razor-pages/
  • .net api + vue.jsdotnet-api-vue/
  • .net + blazordotnet-blazor/
  • laravel + reactlaravel-react/
  • rust + angularrust-angular/
  • go + reactgo-react/

Each sample will provide:

  • Local run instructions (SQLite setup, environment variables for OAuth)
  • Seed script for demo data
  • Migrations (EF Core / Laravel migrations / sqlx/SeaORM/Diesel/etc.)
  • Unit, integration, and minimal E2E tests

Authentication Setup (recommended defaults)

  • Use Auth0 with a single tenant and one Application per sample
  • Configure callback/logout URLs per sample’s dev server
  • Map the provider sub claim to the app’s created_by_user_id

Contributing

  • Start by creating the folder for your chosen stack (see names above)
  • Implement the schema and stories exactly as specified to preserve comparability
  • Keep feature code vertically sliced; keep tests close to features
  • Use SQLite and the shared validation rules

License

This repository is licensed under the terms of the LICENSE file in the root.