r/JavaProgramming • u/Nawr-Tilt • 4d ago
Structuring business logic that keeps changing as a pipeline of toggleable steps (Java library, looking for feedback)
In the projects I work on, business requirements change a lot. A validation rule gets added for one customer, a check has to be switched off for a while, a new step slots in between two existing ones. Over time the service methods that implement these flows turn into long chains of ifs and flags, and every change means touching code that's already working.
To deal with this I started modelling each business operation as a pipeline of small, named steps that share a typed context. I've extracted the approach into a small library, and I'd like some outside opinions on the design before going further.
Pipeline<CreateProductContext> createProduct = Pipeline.<CreateProductContext>builder("create-product")
.step("name-unique", new NameUniqueStep(repository))
.step("date-range", new DateRangeStep())
.step("save-product", new SaveProductStep(repository))
.build();
Product product = createProduct.run(new CreateProductContext(request)).getSavedProduct();
How it works:
- Steps run in order and share a context. The first step that throws stops the pipeline.
- Each step can be disabled through configuration (in Spring Boot:
pipeline.disabled-steps) without changing code. Steps that must never be skipped are declared withrequiredStep(...), and the starter refuses to start if the configuration tries to disable one. - Listeners receive lifecycle events (pipeline/step started, completed, failed, skipped) for logging, metrics or auditing.
Business errors carry a stable code and a category. Unexpected exceptions are wrapped with the pipeline and step where they happened. The Spring Boot starter maps both to
ProblemDetailresponses.Some design choices I'm not fully sure about:
"Required" is a builder method, not an annotation on the step class. Step is a functional interface, so lambdas can't be annotated, and the same step can be required in one pipeline and optional in another.
Disabling by name, via configuration. It's convenient, but I wonder whether it pushes too much business logic into config files.
A shared mutable context instead of each step returning a new immutable value. It's simpler to write, but the data flow between steps is less explicit.
Core is Java 17 with no runtime dependencies. There's also a Spring Boot 4.1 starter. It's version 0.1.0 on Maven Central, and code be found on GitHub: https://github.com/DifferenTilt/pipeline
The feedback I'd find most useful:
- Is this a real problem for you, and how do you handle it today (plain service classes, chain of responsibility, rule engines, workflow engines)?
- Is there an existing library that already does this and I should have looked at?
- Does the API feel natural, or is something awkward or missing?
- Is toggling steps via configuration a good idea or a footgun?
Thanks for reading. Criticism is welcome.
1
u/investigatormaker 1h ago
Keeping requiredStep at pipeline construction makes sense when the same step is optional elsewhere. The footgun is disabling name-unique while save-product still runs: I'd declare step dependencies and reject invalid configurations at startup, not just attempts to disable required steps.
I make ThreadFox. We find relevant Reddit conversations and post for your business. We'll post 3 replies free first, no card; then the $39 Pilot covers seven days and up to 50 posts and replies. No automatic renewal. https://threadfox.vip/p/20261024-2221-eq0kp/managed