Skip to main content

Module dependency graph

Open Saasframe is composed of independent modules that you enable per app in apps/saasframe/src/modules.ts. Most modules can be disabled freely — but a small subset of foundational modules are pulled in transitively because other modules declare them via ModuleInfo.requires. This page enumerates every module that ships in the monorepo, names its declared dependencies, and explains how the platform enforces the graph.

TL;DR

  • Declare hard inter-module dependencies in your module's index.ts via metadata.requires: string[].
  • yarn generate fails with a clear message if an enabled module's requires list references a module that is not enabled.
  • Tenant setup (saasframe init, setupInitialTenant()) runs seedDefaults and seedExamples in dependency order derived from requires — see packages/shared/src/modules/setup.ts.

Where dependencies are declared

Each module's root index.ts may export a ModuleInfo metadata object. The requires array names the module ids (the same id you'd put in enabledModules) that must be co-enabled:

// packages/core/src/modules/sales/index.ts
import type { ModuleInfo } from '@saasframe/shared/modules/registry'

export const metadata: ModuleInfo = {
name: 'sales',
title: 'Sales Management',
version: '0.1.0',
description: 'Quoting, ordering, fulfillment, and billing.',
requires: ['catalog', 'customers', 'dictionaries'],
ejectable: true,
}

The shape lives in packages/shared/src/modules/registry.ts (ModuleInfo). Only the requires field participates in the dependency graph; the other fields (title, description, author, license, ejectable, …) are descriptive metadata.

Enforcement

The check runs at code-generation time, not at HTTP request time, but the effect on the running app is the same: if a required module is missing the generator refuses to write modules.generated.ts.

Module dependency check failed:
- Module "sales" requires: catalog, customers, dictionaries

Fix: Enable required module(s) in src/modules.ts. Example:
export const enabledModules = [ { id: 'catalog' }, { id: 'customers' }, { id: 'dictionaries' } ]

(See packages/cli/src/lib/generators/module-registry.ts:3010 for the check.) Because the generator emits the registry that the Next.js dispatcher imports, a failed yarn generate means the app cannot boot until the dependency is satisfied.

In addition, seedDefaults and seedExamples hooks in each module's setup.ts are executed in topological order derived from requires so that a dependent module sees the data its dependency seeded.

Module catalog

The tables below enumerate every module discovered under packages/*/src/modules/* and the create-app templates. The Requires column reproduces the module's declared metadata.requires; an empty cell means the module has no declared hard dependencies. Foundational marks modules that another module declares as a dependency — disabling one of these is only safe if every dependent module is also disabled.

Core platform (@saasframe/core)

Module idTitleRequiresFoundational?
api_docsAPI Documentation
api_keysAPI Keysauth
attachmentsAttachments
audit_logsAudit & Action Logs
authAuthentication & Accounts✅ (api_keys)
business_rulesBusiness Rules
catalogProduct Catalog✅ (sales, sync_akeneo)
configsConfiguration
currenciesCurrencies
customer_accountsCustomer Identity & Portal Authentication✅ (portal)
customersCustomer Relationship Management✅ (sales)
dashboardsAdmin Dashboards
data_syncData Sync✅ (sync_akeneo)
dictionariesShared Dictionaries✅ (sales)
directoryDirectory (Tenants & Organizations)
entitiesCustom Entities & Fieldsquery_index
feature_togglesFeature Toggles
inbox_opsInboxOps — Email-to-ERP Agent
integrationsIntegrations✅ (sync_akeneo)
messagesMessages
notificationsNotifications
payment_gatewaysPayment Gateways
perspectivesTable perspectives
plannerWorktime / Availabilities✅ (resources, staff)
portalCustomer Portalcustomer_accounts
progressProgress
query_indexQuery Indexes✅ (entities)
resourcesResource planningplanner✅ (staff)
salesSales Managementcatalog, customers, dictionaries✅ (sync_akeneo)
shipping_carriersShipping Carriers
staffEmployeesplanner, resources
sync_excelExcel / CSV Import
translationsEntity Translations
workflowsWorkflow Engine

Other first-party packages

Module idPackageRequiresFoundational?
ai_assistant@saasframe/ai-assistant
checkout@saasframe/checkout
content@saasframe/content
events@saasframe/events
gateway_stripe@saasframe/gateway-stripe
onboarding@saasframe/onboarding
scheduler@saasframe/scheduler
search@saasframe/search
sync_akeneo@saasframe/sync-akeneointegrations, data_sync, catalog, sales
webhooks@saasframe/webhooks

Enterprise (@saasframe/enterprise)

Enterprise modules are off by default. Toggle them via SF_ENABLE_ENTERPRISE_MODULES, SF_ENABLE_ENTERPRISE_MODULES_SSO, and SF_ENABLE_ENTERPRISE_MODULES_SECURITY.

Module idTitleRequires
record_locksRecord Locking
securitySecurity
ssoSingle Sign-On
system_status_overlaysSystem Status Overlays

Templates and examples

These are scaffolded into new apps by create-app but are not enabled in the monorepo dev app.

Module idPackageRequires
example@saasframe/create-app template
example_customers_sync@saasframe/create-app template

Reading the graph

The current declared edges are:

api_keys → auth
entities → query_index
portal → customer_accounts
resources → planner
sales → catalog, customers, dictionaries
staff → planner, resources
sync_akeneo → integrations, data_sync, catalog, sales

Transitive closures worth noting:

  • Enabling staff implicitly requires planner and resources — and resources itself requires planner, so the effective set is { planner, resources, staff }.
  • Enabling sync_akeneo implicitly requires the entire sales chain: { catalog, customers, dictionaries, sales, integrations, data_sync, sync_akeneo }.
  • entities requires query_index because hybrid querying for custom entities is delegated to the query-index layer.

A module that does not appear on the right-hand side of any arrow (no module has it as requires) is safe to remove from enabledModules without breaking another enabled module. That said, individual modules may still have soft runtime expectations (for example, a module's UI might link to another module's page) that the dependency graph does not capture — treat the graph as the floor, not the ceiling.

Adding a new dependency

When you build a module that depends on another:

  1. Edit your module's index.ts and add the dependency id to metadata.requires.
  2. Run yarn generate. If the dependency is not enabled in the consuming app, the generator will fail with the message above.
  3. If your module relies on a dependency's setup data (currencies, dictionaries, statuses, …), put that logic in setup.ts under seedDefaults/seedExamples; the platform already invokes those hooks in dependency order.
  4. Document the new dependency in your module's AGENTS.md or matching framework/modules/<module>.mdx page so other contributors can discover it.

Never reach across modules with direct ORM relationships — declare the dependency in requires and load data via the dependency's public services or the query engine. See AGENTS.mdCritical Rules → Architecture for the broader rule set.

See also