Every module implements ModuleProviderInterface — a single contract that defines schemas, metas, abilities, UI, controllers, and migrations. The ModuleRegistry boots them in dependency order. Remove a module, the rest keep working.
Modules don't hook into the system — they declare what they provide. The ModuleRegistry reads the contract and handles registration, boot sequencing, dependency resolution, and cron scheduling automatically.
1interface ModuleProvider {2 // Identity3 id(): string; // "enravo-sube"4 name(): string; // "Branch Management"5 version(): string; // "1.2.0"6
7 // Data declarations8 schemas(): Schema[]; // → auto-CRUD endpoints9 metas(): MetaDef[]; // → entity field extensions10 settings(): Settings; // → singleton schema (auto)11 abilities(): Ability[]; // → custom abilities12
13 // UI declarations14 ui(): ViewSchema[]; // → view schemas15 pageSchemas(): Page[]; // → page-level contracts16 navigation(): NavItem[]; // → headless menu items17 manifest(): RefData; // → reference data18
19 // Controllers20 restControllers(): Controller[];21 adminControllers(): Controller[];22
23 // Lifecycle24 requires(): string[]; // → dependency IDs25 migrations(): Migration[];26 cronJobs(): CronJob[];27 boot(): void; // → post-registration hook28}From registration to serving — every module follows the same boot sequence. Dependency resolution ensures correct order.
Register
Module discovered from registry or external hook. Provider instantiated
Resolve Dependencies
Two-pass: instantiate all, then check requires(). Unmet deps → module skipped
Boot Provider
Abilities, schemas, metas, UI, settings auto-registered. Then boot() called
Migrate
Version-based migrations run if module version > stored version
Serve
REST controllers registered on rest_api_init. Module is live
Register
Module discovered from registry or external hook. Provider instantiated
Resolve Dependencies
Two-pass: instantiate all, then check requires(). Unmet deps → module skipped
Boot Provider
Abilities, schemas, metas, UI, settings auto-registered. Then boot() called
Migrate
Version-based migrations run if module version > stored version
Serve
REST controllers registered on rest_api_init. Module is live
Six extension points — all declarative, all auto-registered.
Modules declare schemas() → UnifiedDriver generates CRUD endpoints. Two types: singletons (settings stored in options) and collections (full CRUD in custom tables). Abilities auto-derived.
Modules declare metas() → fields auto-injected into entity API responses and UI schemas. Four entity types: order, product, user, session. Per-role read/write filtering via SchemaPipeline.
Modules declare ui() → view schemas injected into page-level contracts at specific injection points. Widget types, layout rules, and data endpoints — all declarative.
Modules declare abilities() → auto-registered and synced to roles on boot. Also auto-derived from schemas and routesMeta. Format enforced: enravo/{module}/{action}.
Modules declare addressFields() → custom fields added to the multi-address system. Country-specific billing, shipping, or compliance fields without core changes.
Modules declare cronJobs() → scheduled tasks auto-registered. Notification module provides OTP delivery, security alerts, email templates, and push notification channels.
Each module is a self-contained provider. Some have database tables, some have assets, some just add abilities. All follow the same interface.
Define data models — CRUD endpoints, validation, and abilities auto-generated via UnifiedDriver.
Add fields to orders, products, users, or sessions via metas — without touching core tables.
Register view schemas, form definitions, and navigation items for headless rendering.
Custom permissions auto-synced to roles. Format enforced: enravo/{module}/{action}.
REST controllers for business logic beyond auto-generated CRUD. Admin and public routes.
requires() ensures modules boot in the right order. Unmet dependencies → module safely skipped.
Version-based database migrations. Only run when module version changes. Dependency-ordered.
cronJobs() auto-registered. Recurring tasks managed by the platform, not the module.
boot() runs after all registrations. Safe to add filters, hooks, and event listeners.