FabricFabricPlatform
Platform referenceEntities

Extending with vertical entities

How a vertical declares its object types, actions, events, and state machines through a FabricModule manifest.

A vertical adds entities to the platform by declaring a FabricModule and registering it at startup. There is no other extension point — no plugin discovery, no implicit registration, no auto-loading.

The module shape

From packages/platform/modules/index.ts:

interface FabricModule<TDb = unknown> {
  namespace: string;                                // "lending"
  version?: string;
  displayName?: string;
  description?: string;
  dependencies?: ModuleDependencyInput[];
  objectTypes?: ObjectTypeRegistrationInput[];
  subjectTypes?: string[];
  idPrefixes?: IdPrefixRegistration[];
  eventTypes?: EventTypeRegistrationInput[];
  actions?: ActionDefinition<TDb>[];
  policies?: unknown[];
  stateMachines?: unknown[];
  displayRenderers?: ModuleDisplayRendererRegistration[];
  adapterDefinitions?: unknown[];
  permissions?: unknown[];
  roles?: unknown[];
  redactionRules?: unknown[];
  dataClassifications?: unknown[];
  retentionRules?: unknown[];
  evidenceRenderers?: unknown[];
}

A minimal example

// packages/lending/index.ts
import type { FabricModule } from "@fabricorg/platform/modules";
import type { Prisma } from "@repo/database";

export const lendingModule: FabricModule<Prisma.TransactionClient> = {
  namespace: "lending",
  objectTypes: ["Vehicle", "Party", "Location", "Document"],
  eventTypes: [
    { eventType: "OfferAccepted", schema: offerAcceptedPayloadSchema, version: 1 },
    { eventType: "OfferDeclined", schema: offerDeclinedPayloadSchema, version: 1 },
  ],
  actions: [acceptOfferAction, declineOfferAction],
  policies: [creditPullConsentPolicy],
  stateMachines: [offerStateMachine],
};

Registration at startup

import { registerFabricModules } from "@fabricorg/platform/modules";
import { messagingModule } from "@repo/messaging";
import { lendingModule } from "@repo/lending";

registerFabricModules([messagingModule, lendingModule]);

registerFabricModules does:

  1. Validates each manifest (namespace format, action ID format, dependencies).
  2. Topologically sorts by dependencies (cyclic dependency → throws).
  3. Registers each module's object types, subject types, event types, policies, state machines, actions, and display renderers — using the platform's registerXIfMissing helpers so re-registration is safe.

Validation rules

Enforced by validateManifest in packages/platform/modules/index.ts:

RuleError
namespace must be /^[a-z][a-z0-9-]*$/Invalid module namespace
objectTypes PascalCase alphanumericInvalid object type
actions[i].actionId matches /^[a-z][a-z0-9-]*\.[a-z][a-z0-9_]*$/Invalid action ID
Action's namespace matches the module's namespacedeclares namespace … but is registered by module …
Action ID starts with <namespace>.must start with module namespace
mutatesDomain: true action with empty emitsEventsmutates domain but emits no events
Display renderer for unknown event typeDisplay renderer registered for unknown event type
Module depends on a missing moduledepends on missing module
Module depends on itselfcannot depend on itself
Dependency cycleCyclic Fabric module dependency detected
Two modules with the same namespaceDuplicate Fabric module namespace

These checks run before any registration — a malformed module cannot partially register.

Module composition

The host app is the only thing that knows the full module list. The platform itself never imports a vertical.

What you cannot do

  • Auto-register on import. Importing @repo/lending does not register the module. The app must call registerFabricModules. This is enforced in code review and by the boundary tests.
  • Register at request time. All registration happens at boot. Per-tenant module enabling (entitlements) is separate — the registry holds all modules, entitlements gate per-tenant use.
  • Register a partial module. Validation runs before any side effects. Either the whole module registers or none of it does.

See also

On this page