The recurring design patterns used throughout J4H β and why each one matters
A repository is an object that provides collection-like access to stored data. Callers ask for entries, summaries, or photos β they never write SQL directly. The repository decides how to store and retrieve the data.
database.py β the Database classadd_entry(), get_entries(), delete_entry() are the repository methods for diary dataapp.py call db.get_entries() β they never call SQL directly
SQLite uses ? as a query placeholder. PostgreSQL uses %s. SQLite auto-increments with AUTOINCREMENT; PostgreSQL uses SERIAL. These two engines have incompatible interfaces. The adapter wraps them both so the rest of the app sees only one interface.
database.py β the self.use_postgres flag throughout every methodplaceholder variable ('%s' or '?') is the adapter in actionSERIAL vs AUTOINCREMENT based on the same flagdict using either RealDictCursor (psycopg2) or sqlite3.Rowlist[dict] regardless of which engine ran the queryPlaceholder: ?
Auto-id: AUTOINCREMENT
Last insert: cursor.lastrowid
Placeholder: %s
Auto-id: SERIAL
Last insert: RETURNING id
A service layer sits between the web layer (routes) and the data layer (database). It holds the business logic β the "how" of doing something, not just the "what." Routes become thin: they parse the request, call a service, and return the response.
summarizer.py β HealthSummarizer class; fhir_service.py β FHIRService classHealthSummarizer.generate_summary(entries, doctor_type, custom_doctor) handles all Claude API logic/api/summary route just fetches entries, calls the service, saves the result β no Claude API code in app.pyFHIRService handles all FHIR HTTP calls and data mapping β routes call fhir.get_patient_vitals()A decorator wraps a function to add behavior before or after it runs β without modifying the function itself. Flask uses decorators for routing. J4H adds its own decorator for authentication.
simple_auth.py β @passcode_required decorator; app.py β @app.route on every route@passcode_required checks the session for a valid passcode before running the route function@app.route('/entries') is Flask's own decorator: it registers the function as the handler for that URL@passcode_required above itJ4H is not a traditional server-rendered app where Flask builds the full HTML for each page. Instead, Flask returns a minimal HTML shell and JavaScript fetches the actual data from a JSON API, then renders it into the DOM. This is the Single-Page Application (SPA) pattern applied to a multi-page app.
entries.html, calendar.html, chart.html, photos.html, etc./entries only call render_template('entries.html') β no database callsfetch('/api/entries?patient_id=...') and renders the results/entries β Flask returns the HTML shell (no diary data)patient_id from localStorage and the AES key from sessionStoragefetch('/api/entries?patient_id=...') β Flask returns JSON with encrypted entries
J4H supports multiple patients sharing a single database. Every record in every table carries a patient_id column. Every API call filters by that ID. The currently selected patient is stored in localStorage on the client.
entries, summaries, photos, family_history), all DB methods, all API routesj4h_current_patient in localStorage as a JSON object with patient_id and patient_name?patient_id=... in the query stringpatient_id to db.get_entries(patient_id=...) β no record from another patient leaks throughlocalStorage keyed by j4h_theme_<patientId>Rather than trusting the server to protect data, this pattern encrypts data in the browser before sending it. The server stores only ciphertext. The decryption key never leaves the client.
static/crypto.js β encryptText() and decryptText()sessionStorage as base64 β wiped when the tab closesENC:<iv_base64>:<ciphertext_base64>Browser encrypts the text β sends ENC:iv:ct to Flask β Flask stores it as-is in PostgreSQL
Flask returns ENC:iv:ct as JSON β Browser decrypts it β Plaintext rendered in DOM
Two separate auth gates enforce the passcode requirement: one on the server (Python decorator) and one on the client (JavaScript redirect). Both must be satisfied to access any protected resource.
@passcode_required in simple_auth.py β protects API routes that return datastatic/auth-check.js β runs at the top of every page, redirects to /passcode if no valid sessionPOST /api/passcode after the passcode is verifiedSome work should happen on a schedule rather than in response to a user request. APScheduler runs background jobs inside the same Heroku dyno as Flask, executing independently of any incoming HTTP traffic.
app.py β APScheduler initialized after Flask startupdaily_alert job: runs every 24 hours, emails the admin only if any service health check is warning or errormonthly_report job: runs on the 15th of each month at 9am UTC β always sends the full status report/health dashboardThreadPoolExecutor β all 7 service checks run in parallel per executionUsers should not be locked in to a single device or deployment. The portable data pattern gives them a self-contained encrypted snapshot of their data that can be restored anywhere the passcode is known.
templates/export.html, templates/import.html, routes /export and /import.j4h file.j4h file is encrypted with the same AES-GCM key β it can only be opened on a device with the correct passcode.j4h file, decrypts it client-side, and renders the entries without writing them back to the server/api/entriesj4h_export_YYYY-MM-DD.j4h.j4h file β JS decrypts and renders it β server never involvedRather than blocking the browser until all data is ready, each page renders its skeleton immediately and shows a loading state while data is fetched asynchronously. This keeps the app feeling fast even on slow connections.
fetch() runsloading="lazy" β only loaded when they scroll into viewAPI keys, database URLs, SMTP credentials, and other secrets must never be committed to version control. The twelve-factor app methodology stores all configuration in environment variables, injected at runtime by the platform.
app.py uses os.getenv() for every secret; .env file (git-ignored) holds local valuesDATABASE_URL β set by Heroku automatically when PostgreSQL is attachedANTHROPIC_API_KEY β Claude AI key stored in Heroku config vars, never in the repoMAIL_USERNAME / MAIL_PASSWORD β Gmail SMTP credentialsHEALTH_ALERT_EMAIL β admin alert destination; feature is disabled if not setpython-dotenv loads a .env file so the same code runs without any changesRepository Β· Adapter Β· Service Layer Β· Decorator Β· SPA Fetch Β· Multi-tenant Β· Encrypt-before-store Β· Auth Gate Β· Background Scheduler Β· Portable Data Β· Progressive Rendering Β· Environment Config