Skip to main content
A microservice in Ooneex is a module that runs as its own process. It is built on the same module system as every other module — it still exports a <PascalName>Module of type ModuleType from @ooneex/module with controllers, entities, middlewares, cronJobs, and events — but instead of being composed into the single app process, it is deployed and started on its own. The main app/API talks to it over the network. The difference is deployment and networking, not the programming model. Like a regular module, a microservice lives under modules/<kebab-name>/. Unlike a regular module, it is not registered into AppModule/SharedModule; it owns its own entrypoint, start hook, Dockerfile, config, port, and roles, and it is declared as a remote dependency the app reaches by URL.

Why microservices

  • Same programming model. A microservice exports the same ModuleType shape from @ooneex/module — controllers, entities, middlewares, cronJobs, events. If you can write a module, you can write a microservice.
  • Independent deployment. It is its own process with its own Dockerfile and src/index.ts entrypoint, so it ships, scales, and restarts on its own schedule — separate from the API.
  • Its own runtime config. Each microservice carries a distinct .env.yml on its own port (the API runs on 3000; each microservice takes the next free port starting at 3001) and its own roles.yml.
  • Network boundary. The app calls the microservice over HTTP, so a fault or slow deploy stays contained behind that boundary instead of taking down the whole app process.
  • One CLI to manage it. Scaffold and tear down a microservice with ooneex microservice:create and ooneex microservice:remove — no manual wiring.

How it works

A microservice reuses the module system: it exports a <PascalName>Module of type ModuleType, the same as a regular module. What changes is everything around the module. It gets its own entrypoint and start hook, runs as its own containerized process on its own port, and is reached by the app over HTTP rather than being composed into AppModule/SharedModule. The app declares it in modules/app/app.yml under a microservices: list and resolves its address from an environment variable.
AspectRegular moduleMicroservice
Programming modelModuleType from @ooneex/moduleSame ModuleType from @ooneex/module
DeploymentComposed into the single app processIts own process, with its own Dockerfile and src/index.ts entrypoint
NetworkingIn-process function callsApp reaches it over HTTP
RegistrationAdded to AppModule / SharedModuleDeclared in modules/app/app.yml under microservices:, wired by env var
PortShares the API on 3000Its own port, next free starting at 3001
Config & rolesShares the app configOwns a distinct .env.yml and roles.yml
A microservice is declared in modules/app/app.yml as a named remote whose URL comes from the environment:
microservices:
  - name: "billing"
    url: MICROSERVICE_BILLING_URL
The module export itself looks like any other module — same ModuleType, same @ooneex/module import:
import type { ModuleType } from "@ooneex/module";

export const BillingModule: ModuleType = {
  controllers: [],
  entities: [],
  middlewares: [],
  cronJobs: [],
  events: [],
};
The extra files a microservice owns — src/index.ts, src/OnAppStart.ts, Dockerfile, .env.yml, roles.yml — are what turn that module into a self-contained, deployable service.

When to reach for one

Reach for a plain module by default: it is composed into the app process and called in-process, which is simpler to build, test, and deploy. Reach for a microservice when a piece of the system needs to deploy, scale, or fail independently of the API — a separate runtime, its own port, its own container, its own release cadence. The code inside stays a ModuleType either way, so the choice is about boundaries, not about rewriting your logic.

Next steps

  • Structure — the files a microservice owns: entrypoint, start hook, Dockerfile, .env.yml, roles.yml.
  • Composition — exporting the ModuleType (controllers, entities, middlewares, cronJobs, events).
  • Networking — declaring it in app.yml, ports, and wiring the URL through env.
  • Create — scaffold one with ooneex microservice:create.
  • Remove — tear one down with ooneex microservice:remove.
  • Module overview — the regular, in-process module it is built on.
  • Deployment — shipping the API and its microservices.