Using plugins ​
Plugins let you add cross-cutting capabilities (like persistence, metrics, or logging) without pushing that code into every business step. You configure when the plugin should run through aspects; the framework generates the integration code and calls it at the right point.
How plugins fit the flow ​
- Steps: your business functions.
- Aspects: declarations that say where and when a plugin should run, such as before or after a step; see the aspect semantics reference for the formal rules.
- Plugins: implementation modules that do the cross-cutting work, such as persistence, cache, telemetry, or logging.
This keeps business logic focused while infrastructure concerns stay declared in configuration.
What you configure ​
You decide:
- Which plugin aspects are enabled.
- Scope:
GLOBALor selectedSTEPS. - Position:
BEFORE_STEPorAFTER_STEP. - Any plugin-specific parameters.
What TPF handles for you ​
At build time and runtime, TPF handles:
- Adapter and client/server code generation.
- Transport-specific integration (gRPC/REST/LOCAL).
- Type-aware side-effect calls.
- Runtime injection for generated plugin code.
Aspect naming and module mapping ​
Aspect names must be lower-kebab-case and map to the plugin module base name. For example, aspect persistence maps to module persistence-svc. This keeps dependency resolution deterministic.
Side-effect transport contract ​
Side-effect plugins are modeled as unary services for the selected transport. TPF generates type-indexed service contracts such as ObservePaymentRecordSideEffectService with shape PaymentRecord -> PaymentRecord. It then inserts them at the configured aspect position.
Build-time requirements ​
- A pipeline YAML config must be discoverable so that output types can be resolved for side-effect adapters. The loader checks module root and
config/forpipeline.yaml,pipeline-config.yaml, or*-canvas-config.yaml. - For gRPC transport, protobuf/descriptor content must include the required
Observe<T>SideEffectServicedefinitions.
Plugin host modules ​
If you want plugin-server artifacts generated in a dedicated module, add a marker class annotated with @PipelinePlugin("name") in that module. That scopes plugin-server generation there and avoids leaking plugin implementation dependencies into regular service modules.
Example ​
Without aspect:
{
"steps": [
{
"name": "ProcessOrder",
"cardinality": "ONE_TO_ONE",
"inputTypeName": "Order",
"outputTypeName": "ProcessedOrder"
}
]
}With persistence aspect:
{
"steps": [
{
"name": "ProcessOrder",
"cardinality": "ONE_TO_ONE",
"inputTypeName": "Order",
"outputTypeName": "ProcessedOrder"
}
],
"aspects": {
"persistence": {
"enabled": true,
"scope": "GLOBAL",
"position": "AFTER_STEP"
}
}
}