Skip to Content
DocsHow It Works — Architecture

Architecture — how annotations work

Data flow

JPA class + Portal annotations MetadataService (startup) │ reads annotations via reflection JSON → /api/portal/metadata Frontend (Next.js) │ dynamically generates: tables, forms, filters, actions Full CRUD interface — zero hand-written React components

How it works

  1. Every JPA entity annotated with @PortalEntity is registered in PortalModuleConfig.
  2. At backend startup, MetadataService scans all registered classes and builds a PortalMetadata JSON object.
  3. The frontend fetches the JSON from /api/portal/metadata and dynamically renders the entire UI.
  4. No per-entity React components are needed.

Key components

ComponentDescription
@PortalEntityRegisters a JPA class with the portal — label, module, icon, tabs, permissions
@PortalFieldDeclares a UI field — renderer, filter, validation, visibility
@PortalRelation + @PortalLookupConfigures ManyToOne / OneToMany relations and pickers
@PortalDependencyConditional visibility and range rules for fields
@PortalActionCustom action buttons backed by CDI handlers
@PortalSecurityJWT role-based access control
PortalModuleConfigRegisters modules and entities — sidebar navigation config

Metadata endpoint

GET /api/portal/metadata

Returns a full JSON describing all entities, fields, relations, actions, and UI config. Supports ETag/304 — the frontend only re-fetches when metadata changes.

Sample response (abbreviated)

{ "modules": [ { "name": "CRM", "label": "CRM", "icon": "users", "entities": [ { "name": "Customer", "label": "Customer", "fields": [ { "name": "name", "label": "Name", "renderer": "TEXT", "filterType": "CONTAINS", "required": true } ], "actions": [], "security": { "viewRoles": [], "editRoles": [] } } ] } ], "ui": { "title": "Quatrion Portal", "theme": { "primaryColor": "#2563eb" } } }

Metadata is built once at startup. Changes to annotations require restarting the backend.

UI rendering flow

When a user opens the Customer entity:

  1. Frontend checks metadata cache (or fetches /api/portal/metadata)
  2. Table — renders columns based on @PortalField.showInTable
  3. Form — renders fields based on @PortalField.renderer and @PortalField.tab
  4. Filters — renders the filter panel based on @PortalField.filterType
  5. Actions — renders buttons based on @PortalAction

All CRUD operations are performed through standardised REST endpoints (/api/portal/data/{entityName}).

Last updated on