<PascalName>Module of type ModuleType from @ooneex/module that declares what the service owns. You list artifact classes in the appropriate array, and the framework resolves and wires each one through the dependency injection container — controllers join the router, middleware joins the pipeline, cron jobs join the scheduler, events join the bus, and entities join the data source.
The one thing that sets a microservice apart is exposure. A plain module is registered into AppModule/SharedModule so the app process can resolve it. A microservice is not — it runs as its own process, with its own DI container, its own port, and its own Dockerfile, and its src/index.ts entrypoint boots the service directly from its own module. Composition is identical; what changes is who runs it.
How it works
The service’sModuleType has one array per artifact kind. Listing a class is the declaration — within the microservice’s process the framework reads the module, registers each class with the container, and applies its decorators.
| Array | What belongs here |
|---|---|
controllers | HTTP controllers from @ooneex/controller and WebSocket controllers from @ooneex/socket. The service registers a route for each @Route.* decorator. |
entities | TypeORM entities the service persists in its own database. |
middlewares | Request and socket middleware that intercept the service’s traffic, registered via @decorator.middleware(). |
cronJobs | Scheduled jobs the service runs on a timer within its process. |
events | Pub/sub event handlers the service reacts to or emits. |
Composing the service module
Build the module up one array at a time. Each array maps to a kind of artifact the service owns — nothing more. A completeBillingModule for a self-contained billing service looks like this.
controllers
Both HTTP and WebSocket controllers go incontrollers. The service registers each one’s routes from its @Route.* decorators.
entities
List the TypeORM entities the service persists. A microservice owns its own database, so these entities back its data source — not a shared one.middlewares
Middleware that intercepts the service’s requests or socket connections goes here, registered via its@decorator.middleware().
cronJobs
Scheduled work the service runs on a timer belongs incronJobs. Each job is registered with the scheduler from its cron expression — and runs inside this service’s process.
events
Pub/sub handlers the service reacts to — or emits — go inevents. Each is registered as a subscriber on the event bus.
Booting from the service’s own module
This is where a microservice diverges from a plain module. A module declares its artifacts and is then registered into a destination module so the app process can resolve them — see module composition, whereOrderModule is spread into AppModule.
A microservice skips that step entirely. It is never registered into AppModule or SharedModule. Instead, the template wires the microservice’s src/index.ts entrypoint to boot the service from its own module, so the service’s process resolves BillingModule directly.
src/OnAppStart.ts runs so the service can perform any setup it needs (connecting to its data source, warming caches) before it begins handling traffic. From there, resolution flows through the microservice’s DI container exactly as it would in any Ooneex app: every class listed in BillingModule is registered with the container, and the framework applies the decorators — controllers join the router (see routing), middleware joins the pipeline, cron jobs join the scheduler, events join the bus, and entities join the data source.
Best practices
- One bounded context per service. A microservice is a vertical slice that owns a single domain end to end. Keep billing in
BillingModule; if a concern belongs to another domain, it belongs to that domain’s service. - Keep the service self-contained. Declare the service’s own entities and let it own its database. A microservice should not reach into another service’s data source or share tables.
- Communicate over the network, not by importing. When the billing service needs data from another service, call it over the network — see networking. Do not import another service’s controllers, entities, or repositories.
- List only what the service owns. An array entry is a declaration of ownership. Resolution happens inside this process, so every class you list must be one this service actually runs.
- Don’t register the service into AppModule. A microservice boots from its own module by design. Wiring it into
AppModuledefeats the isolated-process model. - Prefer the generators. Scaffolding with the
:createcommands (controller:create,service:create,entity:create, …) targets the microservice’s module and scaffolds into itssrc/, keeping the module in sync without manual edits.
Learn more
- Microservice overview — what a microservice is and how it differs from a module.
- Microservice structure — how the service’s files, entrypoint, and
.env.ymlare laid out on disk. - Microservice networking — calling other services over the network instead of importing them.
- Module composition — composing a module that is registered into
AppModulerather than booted on its own. - Dependency injection — how the container resolves and wires declared artifacts.
- Routing — how
@Route.*decorators turn controllers into routes.