Service Registry
The Service Registry is the source of truth for all micro-frontends and backend services on the platform. Shell UI uses it to dynamically load remote modules at runtime.
Service model
| Field | Type | Description |
|---|---|---|
id | uuid | Primary key |
name | text | Display name |
slug | text (unique) | URL-safe identifier |
description | text | Short description |
entryUrl | text | Module Federation entry URL (e.g. https://hrm.venizia.ai/api/hrm-microfrontend.js) |
type | enum | SERVICE or SYSTEM_SERVICE |
status | enum | AVAILABLE, UNAVAILABLE, MAINTENANCE |
createdAt | timestamp | Registration date |
Registration
Via API (manual)
http
POST /api/admin/apps
Authorization: Bearer <shell-jwt> (requires apps:write)
Content-Type: application/json
{
"name": "HRM",
"slug": "hrm",
"description": "Human Resource Management",
"entryUrl": "https://hrm.dev.venizia.ai/api/hrm-microfrontend.js",
"type": "SERVICE",
"status": "AVAILABLE"
}Via entry URL (auto-register)
Shell fetches the entry URL, reads the Module Federation manifest, and extracts metadata:
http
POST /api/admin/apps/register-remote
Authorization: Bearer <shell-jwt> (requires apps:write)
Content-Type: application/json
{ "entryUrl": "https://hrm.dev.venizia.ai/api/hrm-microfrontend.js" }How Shell UI loads remotes
At startup, Shell UI calls GET /api/config/apps (public, no auth) to get the active service list. For each service it calls loadRemoteModule():
typescript
import { loadRemoteModule } from '@module-federation/runtime';
const remote = await loadRemoteModule({
name: service.slug,
entry: service.entryUrl,
exposes: './ShellEntry',
});Each remote must export a ShellEntry component at ./ShellEntry.
Plan-gating
Services are linked to subscription plans via plan_services. A service only appears in GET /api/config/apps for users whose organization is on a plan that includes that service.
subscription_plans ──┐
├── plan_services ── services
organizations ───────┘ (via planId)