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 — DefaultDependencyRegistry checks assignability across all registered instances.

  • By name — if the parameter name matches a registered name exactly, that instance is used.

  • AmbiguousDependencyException is thrown when multiple registered instances match the same type.

  • UnresolvableDependencyException is 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).

Multiple @FlowConfiguration classes

@EnablePipelite(flowConfigurations = {
    OrderFlowConfiguration.class,
    PaymentFlowConfiguration.class,
    AuditFlowConfiguration.class
})

Exception reference

Exception Condition

FlowConfigurationException

The class is missing @FlowConfiguration, cannot be instantiated, or a @DefineFlow method does not return FlowDefinition.

UnresolvableDependencyException

A @DefineFlow parameter type cannot be resolved from the DependencyRegistry.

AmbiguousDependencyException

Multiple registered instances are assignable to the same parameter type.

DuplicateFlowDefinitionException

A flow with the same name has already been registered in the PipeliteContext.

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 = …​).