> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Composition

> Declare the controllers, entities, middlewares, cron jobs, and events a microservice owns in its own ModuleType, then boot it from its own entrypoint.

A microservice is composed exactly like a module: it exports a single `<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's `ModuleType` 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.

```typescript theme={null}
import type { ModuleType } from "@ooneex/module";

export const BillingModule: ModuleType = {
  controllers: [],   // HTTP and WebSocket controllers
  entities: [],      // database entities (TypeORM)
  middlewares: [],   // request/socket middleware
  cronJobs: [],      // scheduled jobs
  events: [],        // pub/sub event handlers
};
```

| 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.                                                                                                   |

Every class you list is resolved through the microservice's DI container. That is what makes a bare array entry enough: the container constructs the class, injects its dependencies, and the framework reads its decorators to plug it into routing, the scheduler, or the event bus — the same resolution model as any Ooneex app, just running in a process of its own.

## 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 complete `BillingModule` for a self-contained billing service looks like this.

### controllers

Both HTTP and WebSocket controllers go in `controllers`. The service registers each one's routes from its `@Route.*` decorators.

```typescript theme={null}
import { InvoiceCreateController } from "./controllers/InvoiceCreateController";
import { InvoiceListController } from "./controllers/InvoiceListController";

export const BillingModule: ModuleType = {
  controllers: [InvoiceCreateController, InvoiceListController],
  entities: [],
  middlewares: [],
  cronJobs: [],
  events: [],
};
```

### 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.

```typescript theme={null}
import { InvoiceEntity } from "./entities/InvoiceEntity";
import { PaymentEntity } from "./entities/PaymentEntity";

export const BillingModule: ModuleType = {
  controllers: [InvoiceCreateController, InvoiceListController],
  entities: [InvoiceEntity, PaymentEntity],
  middlewares: [],
  cronJobs: [],
  events: [],
};
```

### middlewares

Middleware that intercepts the service's requests or socket connections goes here, registered via its `@decorator.middleware()`.

```typescript theme={null}
import { BillingAuthMiddleware } from "./middlewares/BillingAuthMiddleware";

export const BillingModule: ModuleType = {
  controllers: [InvoiceCreateController, InvoiceListController],
  entities: [InvoiceEntity, PaymentEntity],
  middlewares: [BillingAuthMiddleware],
  cronJobs: [],
  events: [],
};
```

### cronJobs

Scheduled work the service runs on a timer belongs in `cronJobs`. Each job is registered with the scheduler from its cron expression — and runs inside this service's process.

```typescript theme={null}
import { InvoiceSweepCron } from "./crons/InvoiceSweepCron";

export const BillingModule: ModuleType = {
  controllers: [InvoiceCreateController, InvoiceListController],
  entities: [InvoiceEntity, PaymentEntity],
  middlewares: [BillingAuthMiddleware],
  cronJobs: [InvoiceSweepCron],
  events: [],
};
```

### events

Pub/sub handlers the service reacts to — or emits — go in `events`. Each is registered as a subscriber on the event bus.

```typescript theme={null}
import { InvoicePaidEvent } from "./events/InvoicePaidEvent";

export const BillingModule: ModuleType = {
  controllers: [InvoiceCreateController, InvoiceListController],
  entities: [InvoiceEntity, PaymentEntity],
  middlewares: [BillingAuthMiddleware],
  cronJobs: [InvoiceSweepCron],
  events: [InvoicePaidEvent],
};
```

The result is a complete vertical slice: one object that declares every artifact the billing service owns, with no wiring code in between.

## 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](/module/composition), where `OrderModule` 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.

```typescript theme={null}
// src/index.ts — the microservice's own entrypoint
import { BillingModule } from "./BillingModule";

// The template boots the service from its own module,
// not from AppModule. This process gets its own DI
// container and listens on the port from .env.yml.
```

At startup, `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](/basics/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](/microservice/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 `AppModule` defeats the isolated-process model.
* **Prefer the generators.** Scaffolding with the `:create` commands (`controller:create`, `service:create`, `entity:create`, …) targets the microservice's module and scaffolds into its `src/`, keeping the module in sync without manual edits.

## Learn more

* [Microservice overview](/microservice/overview) — what a microservice is and how it differs from a module.
* [Microservice structure](/microservice/structure) — how the service's files, entrypoint, and `.env.yml` are laid out on disk.
* [Microservice networking](/microservice/networking) — calling other services over the network instead of importing them.
* [Module composition](/module/composition) — composing a module that is registered into `AppModule` rather than booted on its own.
* [Dependency injection](/advanced/dependency-injection) — how the container resolves and wires declared artifacts.
* [Routing](/basics/routing) — how `@Route.*` decorators turn controllers into routes.
