Skip to main content
A module is a self-contained unit of your application that lives under modules/<name>/. Running ooneex module:create scaffolds a fixed set of files: a definition that lists the module’s artifacts, a config file, a workspace package.json, a tsconfig.json, and a mirrored test. Everything you generate afterward — controllers, entities, services, middleware — lands in predictable subfolders under src/. This page walks through that layout file by file. See Overview for what a module is and Composition for how modules combine.

Layout

A freshly created billing module looks like this:
modules/billing/
├── billing.yml                    # module config (type)
├── package.json                   # workspace package: @module/billing
├── tsconfig.json                  # TypeScript config
├── src/
│   ├── BillingModule.ts           # module definition (ModuleType)
│   ├── controllers/               # @decorator.controller() classes
│   ├── services/                  # injectable services
│   ├── repositories/              # data-access repositories
│   ├── entities/                  # database entities
│   └── middlewares/               # IMiddleware / ISocketMiddleware classes
├── tests/
│   └── BillingModule.spec.ts      # asserts the ModuleType arrays exist
└── bin/                           # optional: seeds and runnable commands
The artifact subfolders under src/ (controllers/, services/, repositories/, entities/, middlewares/) are created on demand as you generate artifacts into the module — a new module starts with just src/<Pascal>Module.ts. The bin/ folder is optional and holds seeds and runnable commands for modules that need them.

Generated files

src/<Pascal>Module.ts — the module definition

The heart of the module. It exports a single ModuleType object whose arrays declare what the module contributes to the application. You register each artifact you create by adding it to the matching array.
import type { ModuleType } from "@ooneex/module";

export const BillingModule: ModuleType = {
  controllers: [],
  entities: [],
  middlewares: [],
  cronJobs: [],
  events: [],
};
Each array maps to a kind of artifact and the src/ subfolder where those artifacts live:
ModuleType arrayArtifactLives under
controllersHTTP / WebSocket controllerssrc/controllers/
entitiesDatabase entitiessrc/entities/
middlewaresRequest middlewaresrc/middlewares/
cronJobsScheduled jobssrc/ (cron job classes)
eventsEvent listenerssrc/ (event classes)
Services and repositories are dependency-injected and resolved through the container, so they live under src/services/ and src/repositories/ without a dedicated ModuleType array. See Controller and Entity for those artifact types, and Middleware for the middleware pipeline.

<name>.yml — module config

Declares the module’s type. The generator writes type: "module":
type: "module"
The type is one of api, microservice, design, spa, sdk, or module, depending on what kind of unit you scaffold.

package.json — workspace package

Each module is its own workspace package named @module/<name>. It starts at version 0.0.1 and ships test and lint scripts:
{
  "name": "@module/billing",
  "version": "0.0.1",
  "scripts": {
    "test": "bun test tests",
    "lint": "..."
  }
}
The @module/billing package name doubles as the import-path alias once it is registered in the root tsconfig.json (see Registration).

tsconfig.json

The module’s TypeScript configuration, extending the workspace defaults.

tests/<Pascal>Module.spec.ts — the mirrored test

Every module ships a test that mirrors the definition and asserts each ModuleType array exists. It guards against a malformed definition — checking that controllers, entities, middlewares, cronJobs, and events are all present on the exported module object. Run it with the package’s test script (bun test tests).

Core modules: app and shared

Two modules are special and exist in every project:
ModuleRole
appThe application root. It composes the other modules and is the default destination for generated artifacts that belong to the running app.
sharedCross-cutting code reused across modules — shared services, middleware, and utilities. It is the default target for several generators (for example, middleware:create defaults to shared).
When you create a module with the app destination, the generator registers it into both AppModule and SharedModule so its artifacts are wired into the application and available to other modules.

Registration

Creating a module is not just file generation — module:create also wires the module into the project:
  • Registers the module into its destination. For the app destination, it adds the module to AppModule and SharedModule.
  • Adds the path alias. The @module/<name> import alias is added to the root tsconfig.json, so other code can import the module by its package name.
  • Adds the commit scope. When a .commitlintrc.ts is present, the module’s scope is added so commits can be scoped to the module.

Next steps

  • Overview — what a module is and why modules exist.
  • Composition — how modules are combined into an application.
  • Create — the module:create command reference.
  • Controller and Entity — the artifacts you register in the definition.
  • Middleware — the request pipeline a module contributes to.