Skip to content

Frontend Architecture - Enterprise Angular SPA (@gercia/app)

The enterprise presentation layer of the GERCIA Platform is engineered as a unified, high-performance Single Page Application (SPA): @gercia/app (apps/app/). In accordance with ADR-008, @gercia/app decommissioned legacy server-side Blazor WebSockets in favor of a modern Angular 22 client runtime executing at the browser edge and hosted globally on Cloudflare Pages CDN.

Technology Layer Selected Framework Architectural Responsibility
Framework & Engine Angular 22 (Zoneless, Signals) Reactive client-side architecture using Standalone Components, reactive Signals, and inject() dependency injection without Zone.js overhead.
Component Suite PrimeNG 22 Enterprise UI components (DataTables, MultiSelect, Dialogs, Toasts, Avatars) natively styled via modern design tokens (Aura / Lara).
Utility Styling Tailwind CSS v4 Zero-runtime CSS engine providing utility classes, dark mode tokens, and unified responsive layout primitives.
Medical Scheduling FullCalendar v6+ (@fullcalendar/angular) Day, Week, Month timegrid views, multi-practitioner resource columns, and drag-and-drop clinical appointment booking.
Decoupled Edge Hosting Cloudflare Pages Sub-second global Anycast CDN delivery with zero origin server overhead, HTTP/3, and automated branch deployments.
API Authentication angular-auth-oidc-client Sovereign OAuth 2.1 with PKCE authenticating against Gercia.Server (OpenIddict STS) with automatic Refresh Token Rotation.
flowchart TD
    subgraph EDGE ["1. Global Anycast Delivery (Cloudflare Pages)"]
        CDN["Cloudflare Edge Anycast CDN<br>(HTTP/3 • Zero Origin Server Ingress)"]
        STATIC["Static Distribution Bundle (dist/)<br>• ESBuild Chunking & Tree-Shaking<br>• Sub-Second Initial Load Worldwide"]
        CDN --> STATIC
    end

    subgraph RUNTIME ["2. Angular 22 Zoneless Client Engine"]
        STANDALONE["Standalone Component Tree<br>(Zero NgModule Declarations)"]
        SIGNALS["Signal-Driven Reactivity<br>(computed(), effect(), signal())<br>Ultra-Low Memory & CPU Footprint"]
        INJECT["Native Dependency Injection<br>(inject() Functional Invocations)"]
    end

    subgraph UI_SYSTEM ["3. Design System & Peripherals"]
        PRIMENG["PrimeNG 22 Component Suite<br>(DataTables, Menus, Modals, Badges)"]
        TAILWIND["Tailwind CSS v4 Engine<br>(Deep Petrol Palette • Dark/Light Modes)"]
        CALENDAR["FullCalendar Scheduling Engine<br>(Multi-Resource Clinical & Lab Columns)"]
    end

    subgraph GATEWAYS ["4. Secure Cloud & Edge Communications"]
        API_GW["Gercia.Server REST Minimal APIs<br>(api.gercia.com.br / TLS 1.3)"]
        STS["OpenIddict Sovereign STS<br>(OAuth 2.1 PKCE • Token Rotation)"]
        HUB_EDGE["GERCIA Hub Workstation Agent<br>(http://127.0.0.1:5050 / RS-232 & Thermal)"]
    end

    STATIC --> RUNTIME
    RUNTIME --> UI_SYSTEM
    RUNTIME <--> STS
    RUNTIME <--> API_GW
    RUNTIME <-->|"Sub-Millisecond Loopback"| HUB_EDGE

2. Dynamic Host-Based Presentation Segregation

Section titled “2. Dynamic Host-Based Presentation Segregation”

The enterprise frontend operates as a single consolidated Angular codebase deployed across multiple public and private subdomains. Rather than building and deploying distinct applications for each vertical, @gercia/app resolves its visual layout, navigational menus, and authentication policies dynamically at runtime based on the HTTP Host header via PortalService (apps/app/src/app/core/portal/portal.service.ts):

flowchart TD
    REQ["Incoming Browser Request<br>(window.location.hostname)"] --> DETECT["PortalService<br>(Evaluates Subdomain & Domain Boundary)"]

    DETECT --> COND{Active Hostname?}

    COND -- "ops.gercia.com.br<br>ops-stg.gercia.com.br<br>(or ops.gercia.dev)" --> PORTAL_OPS["PortalContext: 'operations'<br>• Cloudflare Zero Trust (WARP + Google Workspace SSO)<br>• Zero-Click Authentication via /api/ops/me Edge Function<br>• SaaS Subscriptions, Tenants Management, System Health"]

    COND -- "app.gercia.vet.br<br>app-stg.gercia.vet.br<br>(or vet.gercia.dev)" --> PORTAL_VET["PortalContext: 'veterinary'<br>• OpenIddict STS B2B Authentication (Tenant Scoped)<br>• Emerald Green branding & MAPA/SIPEAGRO workflows<br>• Animal Patients, Breeds, Legal Tutors, Vet Compounding"]

    COND -- "app.gercia.com.br<br>app-stg.gercia.com.br<br>(or app.gercia.dev)" --> PORTAL_APP["PortalContext: 'human' (Default)<br>• OpenIddict STS B2B Authentication (Tenant Scoped)<br>• Petrol Blue branding & CFM/CFO/ANVISA workflows<br>• Medical EHR, FDI/ISO 3950 Odontograms, Galenic Lab SGQ"]

Portal Segregation & Environment Invariants

Section titled “Portal Segregation & Environment Invariants”
  1. Strict Zero-Query-String Override: Portal context resolution relies exclusively on window.location.hostname. Overriding portal roles via query strings (e.g., ?portal=operations) is strictly prohibited, guaranteeing that client users can never cross security boundaries into administrative routes.
  2. Canonical Staging Subdomain Handling: In accordance with the Cloudflare Universal SSL single-level wildcard rule (*.gercia.com.br and *.gercia.vet.br), staging portals strictly use hyphenated subdomains: ops-stg.gercia.com.br, app-stg.gercia.com.br, and app-stg.gercia.vet.br.
  3. Reactive Signals Interface (PortalService):
    • portal(): Signal<PortalContext>: Returns 'human', 'veterinary', or 'operations'.
    • environment(): Signal<PortalEnvironment>: Returns 'production', 'staging', or 'local'.
    • isHuman(): Signal<boolean>: Computed signal indicating whether human healthcare features are active.
    • isVeterinary(): Signal<boolean>: Computed signal indicating whether veterinary features are active.
    • isOperations(): Signal<boolean>: Computed signal indicating whether internal backoffice features are active.
    • isProduction(): Signal<boolean>: Computed signal indicating production environment.
    • isStaging(): Signal<boolean>: Computed signal indicating staging environment.
    • isLocal(): Signal<boolean>: Computed signal indicating local workstation environment.
  4. Local Engineering Wildcard Parity: In local development, host detection resolves ops.gercia.dev, vet.gercia.dev, and app.gercia.dev to their respective portal modes with zero manual configuration.
  5. Chromium HSTS Preload & SSL Invariant (ssl: true on Port 7250): Because the .dev gTLD is preloaded in Chromium’s HSTS list, browsers automatically force HTTPS. In apps/app/angular.json, the dev server is configured with "ssl": true on port 7250 with allowedHosts: [".gercia.dev", "localhost", "127.0.0.1"] to deliver valid TLS and prevent ERR_SSL_PROTOCOL_ERROR.

3. Edge Functions & Zero-Click Backoffice Authentication (ENG-17)

Section titled “3. Edge Functions & Zero-Click Backoffice Authentication (ENG-17)”

For internal operators accessing ops.gercia.com.br or ops-stg.gercia.com.br, @gercia/app enforces Zero-Click Authentication without manual login buttons or forms:

sequenceDiagram
    autonumber
    actor Dev as GERCIA Staff Member
    participant CF_EDGE as Cloudflare Zero Trust Edge
    participant PAGES as Cloudflare Pages (/index.html)
    participant FUNC as Pages Function (/api/ops/me)
    participant AUTH as Angular AuthService (Signals)

    Dev->>CF_EDGE: GET https://ops.gercia.com.br
    Note over Dev,CF_EDGE: Cloudflare Access evaluates WARP Posture + Google Workspace SSO
    CF_EDGE->>PAGES: Proxies authenticated request with CF-Access-Jwt-Assertion
    PAGES-->>Dev: Delivers Angular SPA Bundle
    Dev->>AUTH: App Bootstraps (AuthService.initAuth())
    AUTH->>FUNC: GET /api/ops/me
    FUNC->>FUNC: Inspects cf-access-jwt-assertion & cf-access-authenticated-user-email
    FUNC-->>AUTH: HTTP 200 { authenticated: true, email: "user@gercia.com.br", ... }
    AUTH->>AUTH: Updates state signal: operator = { email, roles }
    AUTH-->>Dev: Renders Backoffice Dashboard Instantly (Zero Clicks)
  1. Cloudflare Pages Function (apps/app/functions/api/ops/me.ts): Executes natively at the Cloudflare Edge Worker runtime. It inspects the cf-access-authenticated-user-email and cf-access-jwt-assertion headers injected by Cloudflare Access. If missing, it returns { authenticated: false }.
  2. Angular AuthService (apps/app/src/app/core/auth/auth.service.ts): Uses modern Angular 22 Signals (state, operator, isAuthenticated, userEmail, isDevMock).
  3. Local Dev Mocking (ops.gercia.dev:7250): When running locally on isLocal() && isOperations(), AuthService automatically simulates authenticated operator session (devsecops@gercia.com.br) with isDevMock: true, completely decoupling local developers from Cloudflare Access Edge infrastructure.
  4. Cloudflare SPA Routing Rule (apps/app/public/_redirects): Includes /* /index.html 200, guaranteeing that deep links, route refreshes, and internal Angular Router state changes never cause HTTP 404 on Cloudflare Pages CDN.

3. Reactive State Management & Component Taxonomy

Section titled “3. Reactive State Management & Component Taxonomy”

@gercia/app enforces modern Angular architecture patterns based on Signals and Smart/Dumb Component Segregation:

flowchart LR
    subgraph SMART ["Smart / Container Components (Pages)"]
        PAGE["PrescriptionPage / OdontogramPage<br>• Injects Feature State Services<br>• Coordinates Route Parameters & Observables<br>• Dispatches API Actions"]
    end

    subgraph STATE ["Feature State Services (Signals)"]
        SVC["PrescriptionStateService<br>• items = signal&lt;PrescriptionItem[]&gt;([])<br>• totalItems = computed(() => items().length)<br>• isLoading = signal&lt;boolean&gt;(false)"]
    end

    subgraph PRESENTATIONAL ["Presentational / Dumb Components"]
        UI1["ToothSurfaceSelectorComponent"]
        UI2["PrescriptionItemRowComponent"]
        UI3["WeighingStatusIndicatorComponent"]
    end

    PAGE <--> SVC
    PAGE -->|"inputs (Signal Values)"| PRESENTATIONAL
    PRESENTATIONAL -->|"outputs (Events)"| PAGE
  1. Signal-Driven Reactivity: All mutable UI state is modeled using Angular Signals (signal<T>(), computed(), and effect()). Components consume state reactively without requiring manual RxJS subscription unsubscriptions (takeUntilDestroyed()).
  2. Zoneless Readiness: By eliminating dependence on Zone.js change detection monkey-patching, components trigger surgical DOM updates precisely at the signal dependency level, reducing memory footprints and battery drain on clinical laptops and tablets.
  3. Standalone Components Only: The entire codebase is 100% standalone (standalone: true). Shared reusable components are imported directly into the imports array of consuming views.

4. Client-Side Security & OpenIddict PKCE Integration

Section titled “4. Client-Side Security & OpenIddict PKCE Integration”

Authentication is orchestrated via angular-auth-oidc-client implementing the OAuth 2.1 Authorization Code Flow with Proof Key for Code Exchange (PKCE):

sequenceDiagram
    autonumber
    actor User as Healthcare Professional
    participant Browser as @gercia/app (Angular 22)
    participant STS as Gercia.Server (OpenIddict STS)
    participant API as Gercia.Server (REST Minimal APIs)

    User->>Browser: Enters Credentials / Selects Provider
    Browser->>Browser: Generates Cryptographic PKCE Code Verifier & Challenge
    Browser->>STS: Authorization Request (response_type=code, code_challenge)
    STS->>User: Renders Login Challenge / TOTP 2FA Verification
    User->>STS: Submits Credentials & Authenticator TOTP Code
    STS-->>Browser: Redirect with Authorization Code
    Browser->>STS: Token Exchange (grant_type=authorization_code, code_verifier)
    STS-->>Browser: Issues Signed Access Token (JWT 15m) + Refresh Token (30d)
    Browser->>Browser: Stores Access Token in Secure In-Memory Session
    
    loop API Interactions
        Browser->>API: HTTP Request with Bearer Access Token
        API-->>Browser: 200 OK (Postgres RLS Enforced)
    end

    Note over Browser,STS: Automatic Silent Refresh Token Rotation prior to 15m expiry
  • Zero Secrets in Frontend: The client possesses no client secrets. All cryptographic authorization relies on short-lived PKCE code verifiers generated via the browser’s native window.crypto.subtle API.
  • Refresh Token Rotation (RTR): Every token refresh invalidates the previous refresh token and issues a new cryptographically bound refresh token, immediately detecting token theft attempts.
  • Role & Scope Guards (AuthGuard, RoleGuard): Angular functional router guards (canActivate: [authGuard, roleGuard]) inspect the decoded claims (role, tenant_id, modules) before activating route transitions.

5. Two-Tier Input Validation & Brazilian Healthcare Standards

Section titled “5. Two-Tier Input Validation & Brazilian Healthcare Standards”

In compliance with Brazilian medical, dental, and pharmacy regulations, @gercia/app enforces comprehensive client-side form validation before transmitting payloads to backend hosts:

  1. ReactiveFormsModule Structured Form Hierarchies: Forms are structured using strictly typed FormGroup<T> and FormArray<T>, eliminating untyped form payloads.
  2. Custom Brazilian Legal & Sanitary Validators:
    • GerciaValidators.cpf: Validates mod-11 check digits on patient and professional Brazilian CPF tax IDs, stripping formatting (., -) automatically.
    • GerciaValidators.cnpj: Validates corporate tax IDs for compounding pharmacies and healthcare clinics.
    • GerciaValidators.cns: Validates 15-digit Brazilian National Health Cards (Cartão Nacional de Saúde - CNS).
    • GerciaValidators.councilNumber: Validates professional credentials against issuing regional councils (CRM, CRO, CRMV, CRF, CREFITO).
  3. Instant Ergonomic Feedback: Forms provide real-time field-level validation feedback utilizing PrimeNG p-message and Tailwind alert states, preventing user submission frustration.

6. Workstation Hardware Edge Integration (GERCIA Hub)

Section titled “6. Workstation Hardware Edge Integration (GERCIA Hub)”

Cleanroom compounding workstations in pharmacies and diagnostic specimen labeling stations require sub-millisecond communication with bench hardware:

flowchart LR
    subgraph BROWSER ["Browser Session (@gercia/app)"]
        WEIGH_COMPONENT["Assisted Weighing Component<br>(Compounding Workstation View)"]
        PRINT_COMPONENT["Thermal Label Printing Service<br>(ZPL / TSPL Formatter)"]
    end

    subgraph HUB ["Local Workstation Agent (GERCIA Hub - Gercia.Edge)"]
        LOOPBACK["Local HTTP/SSE Server<br>http://127.0.0.1:5050<br>(Zero Cross-Origin Restrictions)"]
        SERIAL["Serial Scale Worker<br>(RS-232 Toledo/Gehaka Parser)"]
        SPOOLER["Raw Thermal Spooler<br>(Zebra ZPL / Argox RAW Port)"]
    end

    WEIGH_COMPONENT <== "1. Real-time Weight Stream (SSE / < 1ms)" ==> LOOPBACK
    LOOPBACK <--> SERIAL
    PRINT_COMPONENT -- "2. Silent RAW Print Job (HTTP POST)" --> LOOPBACK
    LOOPBACK --> SPOOLER
  • Zero Cloud Latency: Tare and weight streaming execute 100% locally over loopback (127.0.0.1:5050). A transient internet outage never halts laboratory cleanroom weighing.
  • Silent Printing: Prescription and batch thermal labels print silently in under 50 milliseconds without triggering intrusive browser print preview modals.