Microde is a framework for building software as a collection of composable, independently defined modules rather than committing early to a fixed service topology. It provides the same explicit composition and lifecycle model in TypeScript and Rust, with runtime-specific APIs and consistent dependency, reference, and failure semantics.
For TypeScript:
npm install @microde/applicationFor Rust:
cargo add microde-applicationBoth packages expose the same explicit composition and lifecycle model through their language-native APIs.
Create a module, install it in a service, and run the service:
import {
MicrodeApplication,
type MicrodeContext,
MicrodeModule,
ModuleKind,
} 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();
if (result.error !== undefined) {
console.error(result.error);
}
process.exitCode = result.exitCode;For a finite application task, use run(main) instead. Modules start before the
main callback, and its completion begins orderly shutdown:
const result = await service.run(async (context) => {
await performWork();
});A service initializes and sets up modules in dependency-first order, then tears them down, shuts them down, and cleans them up in reverse order. Lifecycle failures are returned in the execution result after cleanup completes.
Declare ModuleKind.Passive for work whose run() finishes on its own and
ModuleKind.Active for work whose run() finishes only after stop().
Modules receive a MicrodeContext, which exposes only the requestStop() and
panic() operations they need from their owning service.
Named installations return opaque handles that bind relationship slots to exact
module instances. Providers expose typed values through runtime ports;
dependencies determine lifecycle order, while references are available only
during run and do not participate in the dependency graph. Rust exposes the
equivalent model through ModuleHandle, Port, Provider, Dependency, and
Reference.
The Rust runtime can be used like this:
use microde_application::{MicrodeApplication, MicrodeError, MicrodeModule, ModuleFuture, ModuleKind};
struct Greeting;
impl MicrodeModule for Greeting {
const KIND: ModuleKind = ModuleKind::Passive;
fn run(&mut self) -> ModuleFuture {
Box::pin(async { Ok(()) })
}
}
#[tokio::main]
async fn main() -> Result<(), MicrodeError> {
let mut service = MicrodeApplication::new();
service.install(|_| Greeting)?;
service.serve().await?;
Ok(())
}Read the documentation for lifecycle guides, examples, compatibility information, and the complete API reference.
This repository uses pnpm. After installing dependencies with
pnpm install, the main commands are:
pnpm build # Build all workspace packages
pnpm test # Run the test suite
pnpm typecheck # Type-check all workspace packages
pnpm docs:dev # Start the documentation site locally