ArchitecturePipelineStepRegistry.java
package com.taxonomy.architecture.pipeline;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Registry for architecture-view pipeline steps.
*
* <p>All Spring beans implementing {@link ArchitecturePipelineStep} are collected
* here automatically. The registry validates that no two steps share the same
* {@link ArchitecturePipelineStep#id() ID} or
* {@link ArchitecturePipelineStep#order() order} value, then stores them sorted
* by order for deterministic execution.
*
* <p>Use {@link #getEnabledSteps()} to obtain the ordered list of steps that
* should run for each pipeline execution, and {@link #listDescriptors()} to
* enumerate the metadata of all registered steps.
*/
@Service
public class ArchitecturePipelineStepRegistry {
private final List<ArchitecturePipelineStep> orderedSteps;
public ArchitecturePipelineStepRegistry(List<ArchitecturePipelineStep> steps) {
Map<String, ArchitecturePipelineStep> byId = new LinkedHashMap<>();
Map<Integer, ArchitecturePipelineStep> byOrder = new LinkedHashMap<>();
steps.stream()
.map(ArchitecturePipelineStepRegistry::validatedStep)
.sorted(Comparator.comparingInt(ArchitecturePipelineStep::order))
.forEach(step -> {
ArchitecturePipelineStep prevId = byId.putIfAbsent(step.id(), step);
if (prevId != null) {
throw new IllegalStateException(
"Duplicate pipeline step ID '%s' registered by %s and %s"
.formatted(step.id(),
prevId.getClass().getName(),
step.getClass().getName()));
}
ArchitecturePipelineStep prevOrder = byOrder.putIfAbsent(step.order(), step);
if (prevOrder != null) {
throw new IllegalStateException(
"Duplicate pipeline step order %d registered by %s and %s"
.formatted(step.order(),
prevOrder.getClass().getName(),
step.getClass().getName()));
}
});
this.orderedSteps = List.copyOf(byId.values());
}
/**
* Returns all registered steps in ascending order, filtered to those
* where {@link ArchitecturePipelineStep#enabledByDefault()} is {@code true}.
*/
public List<ArchitecturePipelineStep> getEnabledSteps() {
return orderedSteps.stream()
.filter(ArchitecturePipelineStep::enabledByDefault)
.toList();
}
/**
* Returns the descriptors of all registered steps in ascending order,
* including steps that are not enabled by default.
*/
public List<ArchitecturePipelineStepDescriptor> listDescriptors() {
return orderedSteps.stream()
.map(ArchitecturePipelineStep::descriptor)
.toList();
}
// --- Validation helpers -------------------------------------------
private static ArchitecturePipelineStep validatedStep(ArchitecturePipelineStep step) {
if (step == null) {
throw new IllegalStateException(
"A null ArchitecturePipelineStep was passed to the registry");
}
String id = step.id();
if (id == null || id.isBlank()) {
throw new IllegalStateException(
"Pipeline step %s must declare a non-blank ID"
.formatted(step.getClass().getName()));
}
return step;
}
}