Heximon Logo
Capabilities

Capabilities

The capability map — every production concern (events, background jobs, cron, caching, storage, health, notifications, idempotency, security, tracing, OpenAPI, MCP, schema, branded ids) and the one config line each one adds.

Your app already has a module, a controller, a database call. Production adds a short, predictable list of concerns on top — none of them change how you got here. Every capability in this section is the same move:

add a package, add one config line — a plugin in heximon.config.ts and a namespace key in your module's config — then write a class. The compiler discovers the class from that namespace key, not from scanning your codebase, so nothing you write is "magically found" — it's listed, exactly once, where you'd expect to look.

The one move, before and after

Start from the shape heximon create scaffolds — one plugin, nothing else:

heximon.config.ts
import { defineHeximonConfig } from "@heximon/build";
import { HttpPlugin } from "@heximon/http/compiler";

// The capability manifest: one compiler plugin per capability. The entire HTTP layer is the one plugin
// below; everything you add later (events, CQRS, queues, …) is one more entry in the same list.
export default defineHeximonConfig({ plugins: [new HttpPlugin()] });

Add events — a second package, a second plugin, nothing else touched:

heximon.config.ts
import { defineHeximonConfig } from "@heximon/build";
import { EventEmitterPlugin } from "@heximon/events/compiler";
import { HttpPlugin } from "@heximon/http/compiler";

export default defineHeximonConfig({
  plugins: [new HttpPlugin(), new EventEmitterPlugin()],
});

Your module's config then lists the class under the plugin's namespace — one key, e.g. events: { handlers: [OrderPlacedHandler] } — and you write the handler as a plain class. That's the whole pattern; every row below is this same move with a different package and namespace key.

Not every capability needs a plugin at all — several (caching, key/value, blob, notifications, idempotency) are plain injectable providers: you pnpm add the package and list a class in providers, no compiler plugin and no namespace key required. The table below says which is which.

Every capability, one line each

CapabilityPackageThe one config linePage
Events@heximon/eventsplugins: [new EventEmitterPlugin()], module: events: { handlers }Events
Background jobs (queue)@heximon/queueplugins: [new QueuePlugin()], module: queue: { handlers }Queue
Scheduled work (cron)@heximon/scheduleplugins: [new SchedulePlugin()], module: schedule: { handlers }Scheduled Work
Caching@heximon/cacheprovider only: providers: [{ provide: Cache, useFactory: ... }], http: { middlewares: [CacheMiddleware] } / cqrs: { queryInterceptors: [QueryCacheInterceptor] }Caching
Key/value storage@heximon/kvprovider only: providers: [{ provide: Storage, useFactory: ... }]Key/Value Storage
Blob storage@heximon/blobprovider only: providers: [{ provide: BlobStore, useFactory: ... }]Blob Storage
Health & readiness@heximon/healthplugins: [new HealthPlugin()], module: health: { indicators }Health & Readiness
Notifications@heximon/notificationsprovider only: providers: [EmailTransport-satisfying class]Notifications
Idempotency@heximon/events / @heximon/cqrs / @heximon/queue (./idempotency subpath)provider only: list the interceptor directly under the existing events / cqrs / queue namespace, e.g. events: { interceptors: [EventIdempotencyInterceptor] }Idempotency
Security hardening@heximon/http (./security subpath)provider only: providers: [RateLimitMiddleware-satisfying class], applied per-route via middlewaresSecurity Hardening
Observability & tracing@heximon/otelplugins: [new OtelPlugin()], top-level otel: {...} config keyObservability & Tracing
OpenAPI@heximon/openapino plugin — construct new OpenApiGenerator(...) from your own controllerOpenAPI
MCP server@heximon/mcpno plugin — construct new McpServerGenerator(...) from your own controller, mark routes with .mcp({})MCP Server
Schema DTOs@heximon/schemano plugin — class X extends SchemaObject({...}) used directly in a route's body/responsesSchema DTOs
Branded IDs@heximon/primitivesno plugin — type UserId = Branded<string, "UserId">, zero runtime costBranded IDs

Production concerns, not architecture

Everything above makes an app that already works operationally sound — durable retries, observable, cache-fast, rate-limited. None of it asks you to restructure how you model your domain.

When your domain itself starts pushing back — multiple features converging on the same invariant, a process that needs a deadline, consistency that has to cross a service boundary — that's a different tier of problem, and Architecture is the gate that tells you which piece to reach for.

Copyright © 2026