How to use a plugin (Application Developer Guide)
Application developers use plugins by applying them to pipelines via aspects. Aspects are the primary abstraction for adding cross-cutting concerns like persistence, logging, or metrics to your pipeline.
Mental model
Think of plugins as services you want to apply at specific points in your pipeline. Aspects are the configuration mechanism that tells the framework where and how to apply these plugins.
What aspects are
Aspects are configuration elements that:
- Define where plugins apply in your pipeline
- Specify timing (before or after steps)
- Remain separate from your core pipeline logic
Why aspects exist
Aspects separate cross-cutting concerns from your core business logic. This keeps your pipeline steps focused on business transformations while handling persistence, metrics, and other infrastructure concerns declaratively.
How aspects differ from steps
- Steps transform data and are central to your business logic
- Aspects perform side effects and are infrastructure concerns
- Steps are always visible in your pipeline definition
- Aspects are applied during compilation and expand into internal steps
Aspect naming and module mapping
Aspect names must be lower-kebab-case and match the plugin module base name. For example, an aspect named persistence is expected to be implemented by a module named persistence-svc. This naming is used to wire plugin dependencies consistently and avoid ambiguous module resolution.
Side-effect transport contract
Side-effect plugins observe stream elements and are exposed as unary services for the configured transport (gRPC or REST). The framework generates deterministic type-indexed services like ObservePaymentRecordSideEffectService with the signature PaymentRecord -> PaymentRecord, and injects them into the stream after each step.
Build-time requirements
- A pipeline config YAML must be available so the processor can discover step output types for type-indexed side-effect adapters. The loader searches the parent module root and a
config/subfolder forpipeline.yaml,pipeline-config.yaml, or*-canvas-config.yaml. - For gRPC transport, protobuf definitions must include the type-indexed
Observe<T>SideEffectServiceservices for any observed type, and the descriptor set must include those definitions.
Plugin host modules
To generate plugin-server artifacts in a dedicated module, add a marker class annotated with @PipelinePlugin("name") inside that module. This tells the annotation processor to emit plugin-server adapters and producers only there, so regular service modules do not depend on plugin implementations.
Global vs step-scoped aspects
Aspects support two scopes:
- GLOBAL: Applies to all steps in the pipeline
- STEPS: Applies to the configured
targetStepslist in the aspect config
Positioning with BEFORE and AFTER
Aspects can be positioned:
- BEFORE_STEP: Executes before the main step
- AFTER_STEP: Executes after the main step
BEFORE_STEP and AFTER_STEP are applied as configured during compilation.
Example: Pipeline with persistence aspect
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"
}
}
}Deployment options
Plugins can be deployed in different ways:
- Embedded: Run in the same process as the pipeline
- Shared runtime: Multiple pipelines share plugin instances
- Separate service: Plugin runs as an independent service
The choice affects performance, scaling, and operational complexity but doesn't change how you configure aspects.
What application developers control
You decide:
- Which plugins to apply
- Where in the pipeline to apply them (BEFORE/AFTER)
- The configuration parameters for each plugin
What is decided automatically
The framework handles:
- Transport protocols between pipeline and plugins
- Type conversion between domain and transport types
- Code generation for adapters
- Runtime injection of plugin implementations