Skip to main content

Quick start

Create a passive module for a finite task, install it, and run the service:

import {
MicrodeApplication,
type MicrodeContext,
MicrodeModule,
ModuleKind,
port,
} from '@microde/application';

class GreetingModule extends MicrodeModule {
readonly kind = ModuleKind.Passive;

constructor(context: MicrodeContext) {
super(context);
}

async run(): Promise<void> {
console.log('Hello from Microde');
}

async stop(): Promise<void> {}
}

const service = new MicrodeApplication();

service.install((context) => new GreetingModule(context));

const result = await service.serve();
process.exitCode = result.exitCode;

serve() resolves only after the lifecycle has finished, including teardown, shutdown, and cleanup. Lifecycle failures are returned in the result rather than thrown from the returned promise:

const result = await service.serve();

if (result.error !== undefined) {
console.error(result.error);
}

process.exitCode = result.exitCode;

Running an application task​

Use run(main) when the application has a finite main task. Modules start first, and completion or failure of the task begins orderly shutdown.

const result = await service.run(async (context) => {
await performWork();
});

An individual MicrodeApplication can execute only once. Install every module before calling serve() or run(main).

Module constructors should accept MicrodeContext, not the concrete MicrodeApplication class. The context exposes non-blocking requestStop() and panic() operations without coupling the module to lifecycle coordination APIs such as install(), serve(), run(), public stop(), or state.

Named dependencies​

Install named instances and bind relationship slots explicitly before starting the application:

const databasePort = port<Database>('database');
const database = service.install(
'database',
(context) => new DatabaseModule(context),
);
const orders = service.install(
'orders',
(context) => new OrdersModule(context, databasePort),
);
service.bind(orders, 'database', database);

Microde validates all bindings, rejects dependency cycles, and publishes provider resolutions atomically when execution begins. References may be cyclic, but they are not part of lifecycle ordering and cannot be read during setup.