Architecture Overview
MetaOne is a multi-tenant platform that hosts extensions as PF4J plugins. The platform provides a structured runtime for capabilities, events, and UI mounts — without extensions needing to know about each other.
Module Structure
metaone-platform/
├── metaone-sdk/ # Shared contracts: manifests, annotations, interfaces
├── metaone-core/ # Spring Boot host application
├── crm-extension/ # Example PF4J plugin (CRM features)
└── chat-extension/ # Example PF4J plugin (Chat/messaging features)metaone-sdk
The SDK is the shared contract layer. Extensions depend only on the SDK — never on metaone-core. It defines:
- Extension manifest records (
ExtensionManifest,CapabilityManifest,UiManifest, etc.) - Capability contracts (
@CapabilityGroup,@Capability,CapabilityHandler,CapabilityContext,CapabilityResult) - Event contracts (
EventBusGateway,PlatformEventHandler,@EventHandler,EventPayload,PlatformEvent) - Plugin base class (
MetaonePlugin) - Webhook contract (
WebhookHandler)
metaone-core
The Spring Boot host application. It:
- Runs on context path
/api - Hosts and manages PF4J plugins from the configured
plugins/directory - Owns all platform tables (
ext_installation,ext_capability_registry,ext_event_subscription, etc.) - Orchestrates the extension lifecycle: install → enable → disable → update → uninstall
- Routes capability invocations and event dispatch
- Serves UI mount registrations to the frontend shell
Extension Runtime Types
Extensions declare their runtimeType in their manifest:
| Type | Description |
|---|---|
IN_PROCESS | JAR plugin loaded by PF4J into the host JVM. Capabilities invoked via reflection. |
EXTERNAL_SERVICE | Remote HTTP service. Capabilities POSTed to {serviceBaseUrl}/capabilities/{capabilityKey}. Events POSTed to {serviceBaseUrl}/events. |
Capability Routing
Capability routing is registry-driven, not scan-driven.
POST /api/workspaces/{workspaceId}/capabilities/{capabilityKey}
│
▼
CapabilityInvocationService
│
▼
CapabilityRegistryService ──▶ ext_capability_registry (lookup by workspaceId + capabilityKey)
│
▼
CapabilityGatewayFactory
├── IN_PROCESS ──▶ InProcessCapabilityGateway ──▶ PF4J plugin method (via @CapabilityGroup/@Capability)
└── EXTERNAL_SERVICE ──▶ ExternalCapabilityGateway ──▶ HTTP POST to remote serviceEvent Flow
Events are workspace-scoped. Publishing is open to any workspace member.
POST /api/workspaces/{workspaceId}/events/publish
│
▼
EventDispatchService
│
▼
ext_event_subscription (find subscribers for this eventKey + workspaceId)
│
┌────┴─────┐
│ │
▼ ▼
EXTERNAL IN_PROCESS
HTTP InProcessEventGateway
POST └── reflects PlatformEventHandler, finds @EventHandler(eventKey)
└── async = true → runs on eventExecutor thread pool
└── async = false → runs inline (blocks publisher)UI Mount Flow
The frontend shell queries mount registrations at login and builds navigation/routes/widgets dynamically — no hardcoded knowledge of extensions.
GET /api/catalog/workspaces/{workspaceId}/ui-mounts
│
▼
UiMountRegistryService ──▶ ext_ui_mount_registry
│
▼
UiMountBundleDTO { nav[], routes[], widgets[] }Database Architecture
The platform uses a single host database for all platform metadata. Extension-owned data lives in a separate workspace-scoped database per workspace.
| Table | Purpose |
|---|---|
workspace | Tenant/workspace definitions |
workspace_user | Platform users |
ext_installation | Installed extension records |
ext_capability_registry | Registered capability keys per workspace |
ext_event_subscription | Event subscriptions per workspace/extension |
ext_ui_mount_registry | UI nav/route/widget mounts |
ext_audit_log | Extension lifecycle audit trail |
ext_permission_grant | Granted permissions per workspace |
ext_update_history | Extension update history |
Security Model
- JWT-based authentication, issued on login via
/api/auth/login - Platform-level roles:
ADMIN,USER - Workspace-level access checked via
@wsAccess.isMember(workspaceId, authentication)Spring Security expressions - Capability invocation and event publishing require workspace membership or
ADMINrole