FlowConfiguration SPI
The FlowConfiguration SPI provides a structured, declarative way to define and register flows.
Instead of constructing FlowDefinition objects inline and registering them imperatively, you annotate a dedicated class with @FlowConfiguration and each factory method with @DefineFlow.
This mechanism works in both plain-Java and Spring Boot environments with the same annotation model — only the dependency resolution strategy differs.
Annotations
@FlowConfiguration
@FlowConfiguration (package io.pipelite.dsl.annotation) marks a class as a container of flow factory methods discoverable by the framework.
@FlowConfiguration
public class OrderFlowConfiguration {
// @DefineFlow methods
}
@DefineFlow
@DefineFlow (package io.pipelite.dsl.annotation) marks a method that produces a single FlowDefinition.
Each annotated method must return a FlowDefinition built via Pipelite.defineFlow(name)…build().
@FlowConfiguration
public class OrderFlowConfiguration {
@DefineFlow
public FlowDefinition orderFlow() {
return Pipelite.defineFlow("order-flow")
.fromSource("kafka://orders")
.toSink("slf4j://orders-log")
.build();
}
@DefineFlow
public FlowDefinition paymentFlow() {
return Pipelite.defineFlow("payment-flow")
.fromSource("kafka://payments")
.toSink("slf4j://payments-log")
.build();
}
}
A single @FlowConfiguration class can contain multiple @DefineFlow methods.
Dependency resolution
@DefineFlow methods can declare parameters representing collaborators needed to build the flow.
Parameters are resolved through the DependencyRegistry in plain-Java mode, or through the Spring ApplicationContext in Spring Boot mode.
@FlowConfiguration
public class AcquireFlowConfiguration {
@DefineFlow
public FlowDefinition acquireFlow(OrderService orderService) { (1)
return Pipelite.defineFlow("acquire-flow")
.fromSource("http://api/orders")
.process("handle", exchange ->
orderService.handle(exchange.getInputPayloadAs(Order.class)))
.toSink("slf4j://orders")
.build();
}
}
| 1 | OrderService is resolved from the registry at scan time, before the context starts. |
Plain-Java usage
Register dependencies in the PipeliteContext before passing the configuration class.
PipeliteContext context = Pipelite.createContext();
context.registerDependency("orderService", new OrderServiceImpl()); (1)
context.registerFlowConfigurationClass(AcquireFlowConfiguration.class); (2)
context.start();
| 1 | Makes OrderServiceImpl available for @DefineFlow parameter resolution. |
| 2 | FlowConfigurationScanner instantiates the class via its default constructor and invokes each @DefineFlow method. |
DependencyRegistry
DependencyRegistry (package io.pipelite.core.config) is a lightweight lookup store, not a full DI container.
Resolution rules:
-
By type —
DefaultDependencyRegistrychecks assignability across all registered instances. -
By name — if the parameter name matches a registered name exactly, that instance is used.
-
AmbiguousDependencyExceptionis thrown when multiple registered instances match the same type. -
UnresolvableDependencyExceptionis thrown when no match is found.
Spring Boot usage
List the @FlowConfiguration classes in the flowConfigurations attribute of @EnablePipelite:
@SpringBootApplication
@EnablePipelite(flowConfigurations = AcquireFlowConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Spring constructs the @FlowConfiguration class as a regular bean, with full constructor injection support.
FlowConfigurationBeanPostProcessor then scans it, invokes each @DefineFlow method, and registers the resulting FlowDefinition instances in the PipeliteContext.
@FlowConfiguration
public class AcquireFlowConfiguration {
private final OrderService orderService;
public AcquireFlowConfiguration(OrderService orderService) { (1)
this.orderService = orderService;
}
@DefineFlow
public FlowDefinition acquireFlow() {
return Pipelite.defineFlow("acquire-flow")
.fromSource("http://api/orders")
.process("handle", exchange ->
orderService.handle(exchange.getInputPayloadAs(Order.class)))
.toSink("slf4j://orders")
.build();
}
}
| 1 | Standard Spring constructor injection — OrderService must be a Spring-managed bean. |
Alternatively, @DefineFlow method parameters are resolved through the ApplicationContext when using Spring:
@FlowConfiguration
public class AcquireFlowConfiguration {
@DefineFlow
public FlowDefinition acquireFlow(OrderService orderService) { (1)
return Pipelite.defineFlow("acquire-flow")
.fromSource("http://api/orders")
.process("handle", exchange ->
orderService.handle(exchange.getInputPayloadAs(Order.class)))
.toSink("slf4j://orders")
.build();
}
}
| 1 | Resolved via ApplicationContext.getBean(OrderService.class). |
Exception reference
| Exception | Condition |
|---|---|
|
The class is missing |
|
A |
|
Multiple registered instances are assignable to the same parameter type. |
|
A flow with the same name has already been registered in the |
Migration from the old pattern
Prior to 1.0.0-SNAPSHOT, flows were registered directly or declared as @Bean:
// Old plain-Java pattern (still valid for simple inline definitions)
FlowDefinition flow = Pipelite.defineFlow("my-flow")...build();
context.registerFlowDefinition(flow);
// Old Spring pattern — now deprecated
@Bean
public FlowDefinition myFlow() {
return Pipelite.defineFlow("my-flow")...build();
}
The @Bean-returning-FlowDefinition Spring pattern relies on FlowDefinitionRegistrar, which is now @Deprecated and will be removed in a future major release.
Replace it with @FlowConfiguration / @DefineFlow and declare the class in @EnablePipelite(flowConfigurations = …).