Repository navigation
Architecture
Daniel J. Saldaña edited this page Oct 29, 2026
·
1 revision
🇪🇸 Español
Goliat separates collection (an Azure Function) from presentation (a Next.js application backed by PostgreSQL). They communicate through a single versioned JSON document in Blob Storage.
Azure Function (timer / queue / authenticated HTTP)
│ Resource Graph + ARM, managed identity
▼
Blob Storage ── azure/latest.json (schemaVersion 2, ETag)
│ read-only identity
▼
Next.js backend ── normaliser → PostgreSQL snapshot (one transaction)
│
▼
Dashboard (React, shadcn/ui, Recharts) ── Entra ID or demo login
│
└── FinOps → Azure Cost Management (server-side identity, Cost Management Reader)
Interactive diagrams of the application flow and the Azure deployment are available on the website and inside the dashboard. They are rendered with Archify from docs/architecture.json and docs/azure-deployment.json.
| Component | Responsibility |
|---|---|
| Azure Function | Collects resources with Reader permissions, builds and validates the JSON document, publishes it with an ETag condition. See Inventory Collector. |
| Blob Storage | Holds azure/latest.json, deployment packages and the Function's operation state. |
| Next.js backend | Downloads the blob, validates the contract, normalises 73 views and publishes them in one transaction. Serves protected APIs. |
| PostgreSQL | Stores inventory snapshots, Goliat role assignments and their audit trail. |
| Dashboard | React UI in English and Spanish, with charts, search, filters and export. |
- The Refresh button calls
POST /api/inventory/azure/refresh/. - The backend asks the Function for a new generation using an Entra token (role
Inventory.Generate) and returns202with the operation ID. - The UI polls the operation until it is
succeeded. - The backend downloads the blob with the confirmed ETag, normalises it and persists it.
- The UI reloads the views, keeping the subscription, filters and page.
A PostgreSQL advisory lock allows only one manual refresh at a time across all users and replicas. A second request receives 409 refresh_busy.
The Function publishes UTF-8 JSON with schemaVersion: 2:
{
"schemaVersion": 2,
"provider": "azure",
"generatedAt": "2026-10-09T05:00:00.000Z",
"snapshot": {
"catalog": [],
"tagKeys": [],
"reports": [],
"totals": [],
"tags": []
}
}-
reportscontains 69 source reports, all required even when empty. - The backend derives four more views (subscriptions, resource totals and two tag groupings), giving 73 views.
- Unknown versions, duplicate or missing reports are rejected.
- The download uses
If-MatchandAZURE_MAX_OBJECT_MB. If the blob changes during the read or validation fails, the previous snapshot is kept.
Full contract: docs/backend.md and docs/azure-function.md.
| Path | Content |
|---|---|
app/ |
Next.js App Router: pages, metadata, login, protected docs, read and refresh APIs. |
src/ |
Components, hooks, services and i18n catalogues by feature. src/core/ has no React or HTTP. |
server/ |
PostgreSQL pool, schema and migrations, authentication, access control, logging, FinOps, administration and CLI. |
server/modules/azure/ |
Blob reader, normalisers, queries, repositories and seed data. |
functions/ |
Independent Node.js Azure Functions collector. |
iac/ |
Bicep and PowerShell for the Azure deployment. |
docs/ |
Reference documentation and architecture diagrams. |
site/ |
Static product website for GitHub Pages. |
tests/ |
Unit, PostgreSQL integration and Playwright browser tests. |
- The web never runs collectors or migrations at start-up and can be replicated behind a load balancer.
- Migrations run as a separate pre-deployment job; the web process never executes DDL.
- Snapshots are global. Running independent jobs per subscription would replace the visible inventory with partial results.
- A paginated API (
GET /api/inventory/azure/<reportId>/?limit=100) is available for large views; the current UI still uses the full snapshot endpoint.