Architecture
FixPortal separates the public and operator UI, the simulator backend, and content authoring so each can ship on its own cadence without sharing a database or source tree.
Runtime path
The React application is a Vite-built SPA with prerendered public routes. Authenticated commands and queries use REST; live session, order, and traffic updates use SignalR. React Query owns server state, Zustand owns small client preferences, and MSAL supplies Entra authentication in cloud builds.
The .NET 10 backend is an ASP.NET Core minimal API and SignalR host. It owns tenant authorization, SQL Server persistence, simulation policy, and operational endpoints. Its engine and simulation projects wrap QuickFIX/n sessions and turn policy into FIX messages.
The SQL-free Acceptor.Lite project is the demo venue. The browser never calls it: the public demo talks to a separate demo host service (the FixPortal.Demo.Host sample in the engine repository) over its own demo contract, recorded in contracts/demo/demo-host.contract.ts, and that host logs a pool of initiator sessions on to Acceptor.Lite and drives its venue controls over HTTP. That demo contract is separate from the tenant REST contract.
Repository boundaries
- fixportal-simulator-frontend owns the SPA, public pages, docs/blog MDX, generated REST type declarations, and committed consumer contract snapshots.
- fixportal-simulator-backend owns REST, SignalR, DTO, ATDL, persistence, and FIX-runtime contracts.
- fixportal-cms is a Keystatic editor over this repository's
content/directory; it owns no content database. - fixportal-engine remains a separate reusable FIX-engine product; the simulator backend does not depend on it. It hosts the
FixPortal.Demo.Hostsample that the public demo talks to. Do not assume every simulator runtime concern belongs there.
Contracts
The backend publishes OpenAPI, SignalR, TypeScript-shape, and ATDL state-rule snapshots. The frontend commits consumer snapshots under contracts/. npm run generate:rest-types writes REST type declarations to src/api/generated/; it does not generate an HTTP client. Hand-maintained application types in src/api/types.ts form the stable facade consumed by UI code.
The backend's parity tests compare its DTOs with the frontend snapshot, preventing one repository from silently renaming or removing fields.
Content and deployment
Docs and blog entries are MDX compiled into the released frontend. A content change therefore goes through frontend build and prerender checks. The content workflow composes current content with an already released application version, then publishes the resulting static site. Merging an edit does not mutate a live database.
Theme persistence
The published site theme lives in one PlatformSettings row keyed theme_overrides, holding a JSON blob. Saved theme presets live in their own ThemePresets table. Only an unpublished draft touches the browser: it sits in localStorage and is merged over the fetched published theme on boot. White-label snapshot builds skip the runtime fetch and instead bake a committed theme snapshot, exported from ThemeLab into brands/<site>/theme.json, into the static build.
Security boundaries
Tenant identity is enforced by the backend, not trusted from browser state. Customer builds are generated from the same source with site-specific assets and exclusions. Brand-leak tests, auth gates, and server authorization are separate controls; none substitutes for another.
Updated 2026-09-25 · Edit on GitHub