Foundations
Lifecycle & the kernel
The kernel is the initialization dimension: the part that takes a normalised intention and applies your domain to it, once per event, inside a fresh container. It knows nothing of any platform, and it is where the request's short life plays out.
#What the kernel does
The principle
Between "a request arrived" and "a response left" there is a fixed sequence: build a scope, run the middleware, invoke the handler, handle errors, produce a response. Naming that sequence, and keeping it platform-agnostic, is what lets one flow serve every runtime.
In Stone.js
The kernel receives an IncomingEvent from an adapter, creates the per-event container, sends the event through the middleware pipeline to the resolved handler, maps the result (or an error) to a response, and returns it to the adapter. Every context reuses this exact kernel.
The adapter says what happened. The kernel decides what to do about it. Your domain says what it means.
#The application lifecycle
There are two timelines. The first runs once: the app is built, collapses to one context at run time, starts, serves events, and terminates. The long-lived adapter is the only thing that persists across events.
Setup · once
Build the Blueprint
Discovered decorators and define* modules compose the single manifest, once, before any event. Then it freezes.
onPreparingBlueprintonBlueprintPreparedInitialization
Initialize
The container comes up and providers register; the app is wired but not yet listening.
onInitIntegrationrun time
Collapse to one context
StoneFactory.run() resolves a single adapter from the stack: the one whose alias matches the platform, else the default, else the only one. This is the contextual collapse.
Integration
Start & listen
The resolved adapter (the only long-lived thing) starts and begins accepting causes.
onStartFunctional · per event
Handle events
For every cause, the per-event cycle runs (below), then repeats for the life of the process.
Shutdown · once
Terminate
On shutdown the app drains and closes; the adapter stops.
onTerminate
#The per-event lifecycle
The second timeline runs for every cause: from the raw platform event to the native effect, inside a fresh container that is created and discarded per event. The pills mark the hooks that fire at each step.
Integration · adapter
Capture the raw cause
The long-lived adapter receives the platform’s raw event: an HTTP request, a message, an argv, an agent call.
Integration · adapter
Adapter middleware
Runs on the raw cause and response, around the kernel call, for boundary concerns.
onProcessingAdapterMiddlewareonAdapterMiddlewareProcessedIntegration · adapter
Normalise to an intention
The adapter builds an IncomingEvent: transport-agnostic, the form the domain reads.
onBuildingIncomingEventInitialization · kernel
Fresh ephemeral container
The kernel creates a container for this one event and resolves handlers and services into it.
onHandlingEventInitialization · kernel
Kernel middleware pipeline
The event flows through the middleware chain, where validation, auth and authorization attach.
onProcessingKernelMiddlewareonKernelMiddlewareProcessedFunctional · your domain
Run the handler
The resolved handler runs and returns a value (or throws). This is your code.
onExecutingEventHandleronEventHandledInitialization · kernel
On error, map it
A thrown error is routed to the matching error handler and mapped to a response.
onExecutingErrorHandleronHandlingAdapterErrorInitialization · kernel
Prepare the response
The result (or mapped error) becomes an OutgoingResponse; terminating middleware runs.
onPreparingResponseonResponsePreparedIntegration · adapter
Emit the native effect
The adapter turns the response into the platform’s native effect; the ephemeral container is discarded.
onBuildingRawResponse
#Lifecycle hooks
Hooks let you run code at the precise moments above, application startup, shutdown, and around each event, without threading that code through your handlers. Mark a method with @Hook(name) and the kernel calls it at the right time.
import { Hook, StoneApp } from '@stone-js/core'
@StoneApp({ name: 'tasks' })
export class Application {
@Hook('onStart')
async warmUp () { /* open pools, prime caches, once at startup */ }
@Hook('onTerminate')
async drain () { /* flush and close, once at shutdown */ }
}import { defineHookListener } from '@stone-js/core'
// The same two moments, registered imperatively as a list of hook listeners.
export const hooks = [
defineHookListener('onStart', () => { /* open pools, prime caches, once at startup */ }),
defineHookListener('onTerminate', () => { /* flush and close, once at shutdown */ })
]#Two scopes, dimension-bound
Hooks are scoped to the dimension they observe, which sorts them into two lifetimes:
- Global hooks fire once over the app's lifetime (
onInit,onStart,onTerminate), plus the setup pair around the Blueprint (onPreparingBlueprint,onBlueprintPrepared). They live with the long-lived adapter. - Per-intent hooks fire for every event, inside its ephemeral container (
onHandlingEvent,onProcessingKernelMiddleware,onExecutingEventHandler,onEventHandled,onExecutingErrorHandler,onPreparingResponse), from the moment the context is created to when the response is sent and it is torn down.
Hooks exist to observe the lifecycle, not to alter it. To change what happens to an event, use middleware.