Modules and Application Structure

A module groups a business feature with its pages, API routes, data and translations. Your application chooses which modules to load. Generators collect their declarations so the application can find them.

On this page

Where does your code belong?

In a standalone application, keep a custom feature in its own directory under src/modules. Enable it in src/modules.ts. Framework development uses the product repository, where Core modules live under packages/core/src/modules.

Architecture in practiceYour module becomes part of the application at build time

src/modules/service/

backend/Pages in the admin panel
api/HTTP endpoints
data/Entities and validation
subscribers/Reactions to events
src/modules.tsEnable the module
generate
RegistriesConnect files to the app
build
Running application
src/modules/visit_planning/   # example custom module
src/modules.ts              # enabled modules
Code and documentation

generators/module-registry.ts

From source files to a running application

  1. Select modules and declare their dependencies.
  2. Run the application’s generation scripts. Missing declared dependencies stop module registry generation.
  3. Build the application and check that the expected page, route or service is registered.
Code and documentation

generators/module-registry.ts

Example: a service module

A service feature can own its case records, API and panel pages in one module. This illustrative directory shows where those responsibilities go. Add only the files needed by the feature; a module does not need to implement every convention.

src/modules/service/
  index.ts           # module metadata
  acl.ts             # permission identifiers
  di.ts              # service registration
  data/entities.ts   # stored records
  api/               # HTTP routes
  backend/           # admin pages
  subscribers/       # event handlers
  i18n/              # translations
Code and documentation

generators/module-registry.ts · extensibility/current-surfaces.mdx

Why editing a file may not be enough

The application resolves pages, routes and extensions through generated registries. When you add a convention-based file, enable its module and run the generation script provided by your application. Inspect the registry if the feature is missing. Editing the generated output is temporary: the next generation replaces it.

Declaring an entity also affects the database schema. Generate and review its migration, then apply it in the target environment. A successful route build cannot create a missing database table by itself.

Code and documentation

generators/module-registry.ts

How should modules share information?

Use the documented extension surfaces and declared events for cross-module behavior. Keep the interface small: for example, pass a customer identifier and the data the receiving feature needs. Importing another module’s private implementation makes future changes harder to isolate.

Code and documentation

extensibility/current-surfaces.mdx · events/factory.ts

Before upgrading

Keep your changes in source files and regenerate derived registries. Test the features that depend on changed modules. A module boundary helps organize ownership, but it does not ensure that your customization will survive every upgrade unchanged.

Code and documentation

generators/module-registry.ts · extensibility/current-surfaces.mdx

Sources

Open Mercato source used in these explanations