Angular with standalone components and signals. Port 4200 in development, static
objects behind CloudFront in production. Four screens, two API clients, one interceptor.
The frontend is the only part of the platform that runs on hardware you do not control, and every
design decision here follows from that. It holds a token, not a credential. It attaches that token in
exactly one place, so there is one line of code to audit rather than one per request. It guards routes,
but treats the guard as a courtesy to the user rather than as security, because the real check happens
in the Trade REST API where an attacker cannot edit the code.
There are no NgModules in this project. Providers are registered in app.config.ts and
each screen is loaded with loadComponent, so the sign-in bundle does not carry the
dashboard, the order ticket and the blotter with it. That matters on a Sprint 11 deployment where the
first paint comes over a CDN to a browser that has cached nothing.
File names are the ones in ui/src/app. The two outbound boxes are the only two
origins this application is allowed to call.
The four screens
Route
Screen
Reads
Writes
/login
Sign in
nothing
POST /auth/login, then stores the token pair
/dashboard
Dashboard
account, balance, positions
nothing
/orders/new
Order ticket
order history, while the new order is still NEW
POST /api/v1/orders
/orders
Blotter
order history
DELETE /api/v1/orders/{id} to cancel a working order
Handling an order that is not filled yet
From Sprint 7 the API answers POST /api/v1/orders with status: NEW, and the
fill lands some time later. The order ticket therefore re-reads order history on an interval, ten times
at two second spacing, and then tells the user to check the blotter rather than spinning forever.
Handle both shapes. In Sprint 6 the same endpoint returns FILLED or
REJECTED in the original response, and the contract permits both. A screen written against
only one of them breaks on the sprint boundary.
What the interceptor is for
One functional interceptor sets the Authorization header, and no component and no
service builds that header itself. That gives you one place to add the token, one place to notice a
401, and one place to trigger a refresh. It also means a security review reads one file
instead of grepping the whole application for the word "Bearer".