| ← Back to JavaScript Development | ← Back to Table of Contents |
10.1 Frontend architecture reference
This page is the compact architecture companion to JavaScript development. It preserves the historical filename while keeping implementation detail in the source and the canonical guide.
Decisions
| Decision | Current contract |
|---|---|
| Build tool | Vite, configured by app/vite.config.js |
| Module format | ES modules ("type": "module" in app/package.json) |
| Behavior framework | Stimulus controllers registered through window.Controllers |
| UI framework | Bootstrap 5 imported by app/assets/js/index.js and bundled |
| Partial navigation | Turbo Frames and Streams |
| Full navigation | Normal browser navigation; Turbo Drive is disabled |
| Shared assets in templates | Logical names resolved by App\View\Helper\ViteHelper |
| Core controller discovery | assets/js/controllers/**/*-controller.js |
| Plugin controller discovery | plugin assets/js/controllers; uppercase Assets remains supported for established code |
| Frontend tests | Jest/jsdom under app/tests/js; Playwright for browser flows |
Boot sequence
Vite controllers entry
├─ import core/plugin controller modules
├─ import shared *-service.js modules
└─ controller modules populate window.Controllers
Vite index entry
├─ import Stimulus, Turbo, Bootstrap, utilities, accessibility, timezone
├─ set Turbo.session.drive = false
├─ Application.start()
├─ register window.Controllers
├─ reconnect missed scopes after Turbo renders
└─ initialize Bootstrap tooltips
The default CakePHP layout emits the controllers entry before index.
Controller registration is a module side effect, so every controller must assign
its public identifier before the application entry enumerates the registry.
Shared global surface
The app intentionally exposes a small compatibility surface:
| Global | Purpose |
|---|---|
window.Stimulus |
Started Stimulus application |
window.Controllers |
Controller classes awaiting registration |
window.bootstrap |
Imported Bootstrap module |
window.KMP_utils |
URL parameter and small escaping helpers |
window.KMP_accessibility |
Accessible dialogs and announcements |
window.KMP_Timezone |
Client timezone formatting/input helpers |
window.urlRoot |
CakePHP-generated application root |
Do not grow this list for feature-local state. Prefer imports, Stimulus values, outlets, or namespaced DOM events.
Server/client boundary
CakePHP remains authoritative for:
- tenant resolution from the request host;
- authentication and authorization;
- policy scopes and data filtering;
- validation and persistence;
- routes and CSRF protection;
- escaped initial markup and Turbo responses.
Stimulus owns transient interaction state and enhancement. Never use a hidden button, missing controller, disabled client flag, or tenant ID in JavaScript as a security boundary.
Entry points versus modules
Add normal controllers and *-service.js modules under the existing discovered
paths. Add a Vite entry only when a layout needs an independently loadable asset.
Every entry increases manifest and delivery complexity.
Current JavaScript entries are:
assets/js/index.js;assets/js/controllers-entry.js.
CSS entries are listed in app/vite.config.js, including core layouts,
workflow designer, Gatherings public UI, Waivers plugin styles, and Drawflow.
The current list is maintained in Asset management.
Compatibility cautions
- Do not add a second asset pipeline or bypass the shared manifest helper.
- Do not enable Turbo Drive without a focused compatibility review.
- Do not hard-code generated hashes or assume one JavaScript output file.
- Do not import Bootstrap separately in templates.
- Do not rely on controller filename alone; the
window.Controllerskey is the public identifier. - Do not omit
disconnect()cleanup because frame navigation reconnects scopes. - Do not build cross-tenant URLs in the browser; use same-origin CakePHP URLs.