Optimizations Azure: Service Architecture¶
This document describes the intended architecture for the optimizations-azure service and its boundaries with the backend API. It serves as the north-star reference for the ongoing refactor.
Diagram¶
flowchart TD
FE["Frontend\n(React, port 3000)"]
BE["Backend API\n(Express, port 3001)\nOwns: users · settings · initiatives"]
OA["optimizations-azure\n(Express, port 3002)\nOwns: all az_* tables · run_logs · pricing"]
SCH["Scheduler Worker\n(pg-boss, separate process)\nRuns: inventory · metrics · recommendations · pricing"]
DB[("PostgreSQL\nstrawly")]
FE -- "JWT (HTTPS)" --> BE
BE -- "reads az_* tables\n(Prisma, read-only)" --> DB
BE -- "writes: users · settings · initiatives\n(Prisma, read-write)" --> DB
BE -- "X-Internal-Token\nPATCH /recommendations/:type/:id/status\nPOST /exemptions\nDELETE /exemptions/:id\nPOST /collectors/:sub/run\n..." --> OA
OA -- "reads + writes az_* tables\nrun_logs · pricing · exemptions\nrecommendation status events\n(Prisma, owner)" --> DB
SCH -- "reads cron config from settings\nwrites inventory · metrics\nrecommendations · run_logs\n(Prisma, owner)" --> DB
OA -. "pg-boss queue\n(same DB)" .-> SCH
Service ownership boundaries¶
Backend owns the user-facing domain: authentication, user accounts, organisations, initiatives, and the settings table. It reads az_* tables from the database to serve inventory and recommendation data to the frontend, but it never writes to those tables directly. Any mutation to opt-azure-owned data goes through an internal HTTP call to the optimizations-azure service.
optimizations-azure owns all Azure-specific data: inventory snapshots, daily metrics, recommendations, pricing, exemptions, run logs, and recommendation status events. It exposes HTTP endpoints (protected by X-Internal-Token) for the backend to trigger mutations. The Express server handles only HTTP; it does not run any scheduled work.
Scheduler Worker is a separate Node.js process that runs alongside the Express server. It uses pg-boss to persist job state in PostgreSQL, so a crash does not lose scheduled runs. It reads cron expressions from the settings table on startup and re-syncs them every minute. The worker and the Express server share the same database but are independent processes with independent lifecycles.
Inter-service auth¶
All calls from the backend to optimizations-azure carry an X-Internal-Token header. The token value is read from process.env.INTERNAL_API_TOKEN at request time (not cached at startup), so it can be rotated by updating the environment variable without a service restart. Both services must be configured with the same token value. The token must be set explicitly in every environment; there is no code-level fallback.
What this fixes¶
| Problem | Fix |
|---|---|
| Static token with baked-in dev fallback | Token read at request time; startup fails if unset |
| Duplicate schema.prisma files | Single canonical copy in shared/prisma/schema.prisma |
| Backend writes to opt-azure-owned tables | Backend calls internal HTTP endpoints instead |
| Scheduler in same process as HTTP server | Scheduler runs as a separate pg-boss worker process |
| Settings table with no ownership contract | Backend validates on write; opt-azure validates on read |
| ``` |