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:
- Validates each manifest (namespace format, action ID format, dependencies).
- Topologically sorts by
dependencies(cyclic dependency → throws). - Registers each module's object types, subject types, event types, policies, state machines, actions, and display renderers — using the platform's
registerXIfMissinghelpers so re-registration is safe.
Validation rules
Enforced by validateManifest in packages/platform/modules/index.ts:
| Rule | Error |
|---|---|
namespace must be /^[a-z][a-z0-9-]*$/ | Invalid module namespace |
objectTypes PascalCase alphanumeric | Invalid 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 namespace | declares namespace … but is registered by module … |
Action ID starts with <namespace>. | must start with module namespace |
mutatesDomain: true action with empty emitsEvents | mutates domain but emits no events |
| Display renderer for unknown event type | Display renderer registered for unknown event type |
| Module depends on a missing module | depends on missing module |
| Module depends on itself | cannot depend on itself |
| Dependency cycle | Cyclic Fabric module dependency detected |
| Two modules with the same namespace | Duplicate 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/lendingdoes not register the module. The app must callregisterFabricModules. 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
- Module contract — how-to guide with a worked example.
- Vertical extensions pattern — design checklist for a new module.
- Built-ins — what is already pre-registered by the platform.