Skip to main content

Applications Overview

The AquaGen codebase is one Nx monorepo that builds five separate web applications. They share the same libraries but ship as different products, to different customers, at different URLs.

This page is the map. Read it first, then dive into Products, Standalone Apps, or Deployment & Environments.

One repo, many apps

Every app is a plain React single-page app built with Rspack. They are not wired together with Module Federation — that is turned off everywhere. See Module Federation Status for details.


The five apps at a glance

App folderShips asRoleBuild output
productionAquaGenMain product — the full platformdist/apps/production
demoAquaGen DemoSales/demo build of the main productdist/apps/demo
uwmsAquaRecycleStandalone product (urban water / recycle)dist/apps/uwms
lakepulseLakepulseStandalone product (lake monitoring)dist/apps/lakepulse
rwiAquaRainStandalone product (rainwater)dist/apps/rwi

Each real app also has a matching end-to-end test app (production-e2e, demo-e2e, uwms-e2e, lakepulse-e2e, rwi-e2e). Those only run Cypress/Playwright tests and never ship.


Main app vs. standalone apps

There are two kinds of app in the repo.

The main app (production, and its clone demo)

production is the full AquaGen platform. It carries every feature — dashboards, all monitoring types, leadership, SCADA, HMI, energy, all the water indices, and more. demo is a near-identical copy used for sales demos (it drops real login and adds a couple of demo-only pages like trueCost).

The main app decides what to show at runtime, based on the logged-in account's permissions. It uses the generic navigation helper and shows the whole route table.

Standalone apps (uwms, lakepulse, rwi)

Each standalone app is a single, focused product built from a small slice of the same libraries:

  • AquaRecycle (uwms) is built around the Used Water Index (uwi).
  • Lakepulse (lakepulse) is built around lake monitoring (ilm).
  • AquaRain (rwi) is built around the Rainwater Monitoring (rwi).

A standalone app has its own smaller route table, its own navigation helper, and tells the shared store which product it is. The mechanics of how that works — and how the pattern has evolved — are explained in Standalone Apps.

What makes an app "standalone" is not Module Federation

It is three small things in the app's startup code: it calls createValidateLogin(<AppNavigationHelper>), it passes standaloneAppName="<app>" into the shared store, and it hands its own navInstance to the layout. Nothing is loaded remotely. See Standalone Apps.


How the apps compare

productiondemouwmslakepulserwi
Product nameAquaGenAquaGen DemoAquaRecycleLakepulseAquaRain
ScopeFull catalogFull catalog + demo pagesuwi verticalilm (lake) verticalrwi vertical
Startup patternLegacy (double-nested)Legacy (double-nested)Current (clean)Current (cleanest)Hybrid (half-migrated)
Login (MSAL)YesNoYesYesYes
Navigation helperGeneric (implicit)Generic (implicit)UwmsNavigationHelperLakePulseNavigationHelperRwiNavigationHelper
Reports libraryreportsV2reportsV2reports (v1)reports (v1)reports (v1)
Route count (approx.)~47 (superset)~46~22~14 (smallest)~20
Dev server port42004200420042004202

"Startup pattern" is explained in Standalone Apps → How the pattern evolved. In short, production and demo still use the old pattern, uwms and lakepulse use the clean new one, and rwi is mid-migration.


Naming conventions

The repo uses a few naming systems that are easy to confuse. Keep this table handy.

ThingConventionExamples
App folder / Nx projectshort lowercaseproduction, demo, uwms, lakepulse, rwi
Product / brand name (the <title>)marketing nameAquaGen, AquaRecycle, Lakepulse, AquaRain
Library import path@aquagen-mf-webapp/<lib>@aquagen-mf-webapp/shared, @aquagen-mf-webapp/monitoring
Firebase hosting targetenvironment/productdev, pre-prod, aquarecycle, rwi-dev
Firebase site id<name>-aquagen (mostly)dev-aquagen, aquarecycle-aquagen, rainwater-dev
Navigation helper<App>NavigationHelperUwmsNavigationHelper, RwiNavigationHelper
App name ≠ product name ≠ index name

The uwms app ships as the AquaRecycle product and is built around the uwi (Used Water Index) library. The rwi app ships as AquaRain and is built around the rwi library. The <title> in each app's index.html is the source of truth for the product name.

The @aquagen-mf-webapp/ import scope is historical — "mf" stands for Module Federation, which the project was originally built around but no longer uses. The scope name stayed even though the architecture changed.


Where things live

aquagen_web_appp/
├── apps/
│ ├── production/ # AquaGen (main) → dist/apps/production
│ ├── demo/ # AquaGen Demo → dist/apps/demo
│ ├── uwms/ # AquaRecycle → dist/apps/uwms
│ ├── lakepulse/ # Lakepulse → dist/apps/lakepulse
│ ├── rwi/ # AquaRain → dist/apps/rwi
│ └── *-e2e/ # test-only apps (never shipped)
├── libs/ # shared + feature libraries (see Architecture → Libraries)
├── bash-scripts/ # build & deploy scripts (see Deployment & Environments)
├── firebase.json # Firebase hosting config (target → build output)
├── .firebaserc # Firebase hosting targets → site ids
└── .github/workflows/ # CI/CD (Azure deploy for production)

Inside each app the important startup files are:

FilePurpose
src/main.jsxOne line: import('./bootstrap'). A leftover async boundary from the old Module Federation setup.
src/bootstrap.jsxThe real entry point — sets up providers, login, and the router.
src/app/app.jsxThe app shell. In the current pattern it is just the router; in the legacy pattern it also re-declares providers.
src/routes/routes.jsThe app's route table.
rspack.config.jsThe build config Nx actually uses (output path, library aliases).
rspack.config.prod.jsNot used — dead config, see Legacy & Deprecated.

Next steps