11 KiB
11 KiB
Pipeline Package Specification
Overview
The Pipeline package provides a robust implementation of the pipeline pattern, allowing for the sequential processing of tasks through a series of stages. It integrates deeply with our Route, Bus, and Queue packages while maintaining Laravel compatibility.
Related Documentation
- See Laravel Compatibility Roadmap for implementation status
- See Foundation Integration Guide for integration patterns
- See Testing Guide for testing approaches
- See Getting Started Guide for development setup
- See Contracts Package Specification for pipeline contracts
Core Features
1. Pipeline Base
/// Core pipeline class with conditional execution
class Pipeline<TPassable> with Conditionable<Pipeline<TPassable>> {
final Container _container;
final List<Pipe<TPassable>> _pipes;
TPassable? _passable;
String _method = 'handle';
Pipeline(this._container, [List<Pipe<TPassable>>? pipes])
: _pipes = pipes ?? [];
/// Sends an object through the pipeline
Pipeline<TPassable> send(TPassable passable) {
_passable = passable;
return this;
}
/// Sets the stages of the pipeline
Pipeline<TPassable> through(List<dynamic> pipes) {
for (var pipe in pipes) {
if (pipe is String) {
// Resolve from container
_pipes.add(_container.make(pipe));
} else if (pipe is Type) {
_pipes.add(_container.make(pipe));
} else if (pipe is Pipe<TPassable>) {
_pipes.add(pipe);
} else if (pipe is Function) {
_pipes.add(FunctionPipe(pipe));
}
}
return this;
}
/// Sets the method to call on the pipes
Pipeline<TPassable> via(String method) {
_method = method;
return this;
}
/// Process the pipeline to final result
Future<TResult> then<TResult>(
FutureOr<TResult> Function(TPassable) destination
) async {
var pass = _passable;
if (pass == null) {
throw PipelineException('No passable object provided');
}
// Build pipeline
var pipeline = _pipes.fold<Function>(
destination,
(next, pipe) => (passable) =>
_container.call(() => pipe.handle(passable, next))
);
// Execute pipeline
return await pipeline(pass);
}
}
2. Middleware Pipeline
/// HTTP middleware pipeline with route integration
class MiddlewarePipeline extends Pipeline<Request> {
final Router _router;
MiddlewarePipeline(Container container, this._router)
: super(container);
/// Adds route-specific middleware
MiddlewarePipeline throughRoute(Route route) {
// Get global middleware
var middleware = _router.middleware;
// Add route middleware
if (route.middleware.isNotEmpty) {
middleware.addAll(
route.middleware.map((m) => _container.make<Middleware>(m))
);
}
// Add route group middleware
if (route.group != null) {
middleware.addAll(route.group!.middleware);
}
return through(middleware);
}
/// Processes request through middleware
Future<Response> process(
Request request,
FutureOr<Response> Function(Request) destination
) {
return send(request)
.when(() => shouldProcessMiddleware(request))
.then(destination);
}
/// Checks if middleware should be processed
bool shouldProcessMiddleware(Request request) {
return !request.attributes.containsKey('skip_middleware');
}
}
3. Bus Pipeline
/// Command bus pipeline with handler resolution
class BusPipeline<TCommand> extends Pipeline<TCommand> {
final CommandBus _bus;
BusPipeline(Container container, this._bus)
: super(container);
/// Processes command through pipeline
Future<TResult> process<TResult>(
TCommand command,
[Handler<TCommand>? handler]
) {
// Resolve handler
handler ??= _resolveHandler(command);
return send(command).then((cmd) =>
handler!.handle(cmd) as Future<TResult>
);
}
/// Resolves command handler
Handler<TCommand> _resolveHandler(TCommand command) {
if (command is Command) {
return _container.make(command.handler);
}
var handlerType = _bus.handlers[TCommand];
if (handlerType == null) {
throw HandlerNotFoundException(
'No handler found for ${TCommand}'
);
}
return _container.make(handlerType);
}
}
4. Job Pipeline
/// Queue job pipeline with middleware
class JobPipeline extends Pipeline<Job> {
final QueueManager _queue;
JobPipeline(Container container, this._queue)
: super(container);
/// Processes job through pipeline
Future<void> process(Job job) {
return send(job)
.through(_queue.middleware)
.then((j) => j.handle());
}
/// Adds rate limiting
JobPipeline withRateLimit(int maxAttempts, Duration timeout) {
return through([
RateLimitedPipe(maxAttempts, timeout)
]);
}
/// Prevents overlapping jobs
JobPipeline withoutOverlapping() {
return through([WithoutOverlappingPipe()]);
}
}
5. Pipeline Hub
/// Manages application pipelines
class PipelineHub {
final Container _container;
final Map<String, Pipeline> _pipelines = {};
final List<Pipe> _defaults = [];
PipelineHub(this._container);
/// Gets or creates a pipeline
Pipeline pipeline(String name) {
return _pipelines.putIfAbsent(
name,
() => Pipeline(_container, [..._defaults])
);
}
/// Gets middleware pipeline
MiddlewarePipeline middleware() {
return pipeline('middleware') as MiddlewarePipeline;
}
/// Gets bus pipeline
BusPipeline bus() {
return pipeline('bus') as BusPipeline;
}
/// Gets job pipeline
JobPipeline job() {
return pipeline('job') as JobPipeline;
}
/// Sets default pipes
void defaults(List<Pipe> pipes) {
_defaults.addAll(pipes);
}
}
Integration Examples
1. Route Integration
// In RouteServiceProvider
void boot() {
router.middleware([
StartSession::class,
VerifyCsrfToken::class
]);
router.group(['middleware' => ['auth']], () {
router.get('/dashboard', DashboardController);
});
}
// In Router
Future<Response> dispatch(Request request) {
var route = matchRoute(request);
return container.make<MiddlewarePipeline>()
.throughRoute(route)
.process(request, (req) => route.handle(req));
}
2. Command Bus Integration
// In CommandBus
Future<TResult> dispatch<TResult>(Command command) {
return container.make<BusPipeline>()
.through([
TransactionPipe(),
ValidationPipe(),
AuthorizationPipe()
])
.process<TResult>(command);
}
// Usage
class CreateOrder implements Command {
@override
Type get handler => CreateOrderHandler;
}
var order = await bus.dispatch<Order>(
CreateOrder(items: items)
);
3. Queue Integration
// In QueueWorker
Future<void> process(Job job) {
return container.make<JobPipeline>()
.withRateLimit(3, Duration(minutes: 1))
.withoutOverlapping()
.process(job);
}
// Usage
class ProcessPayment implements Job {
@override
Future<void> handle() async {
// Process payment
}
}
await queue.push(ProcessPayment(
orderId: order.id
));
Testing
void main() {
group('Middleware Pipeline', () {
test('processes route middleware', () async {
var pipeline = MiddlewarePipeline(container, router);
var route = Route('/test', middleware: ['auth']);
var response = await pipeline
.throughRoute(route)
.process(request, handler);
verify(() => auth.handle(any, any)).called(1);
});
});
group('Bus Pipeline', () {
test('resolves and executes handler', () async {
var pipeline = BusPipeline(container, bus);
var command = CreateOrder(items: items);
var result = await pipeline.process<Order>(command);
expect(result, isA<Order>());
verify(() => handler.handle(command)).called(1);
});
});
}
Next Steps
- Add more middleware types
- Enhance bus pipeline features
- Add job pipeline features
- Improve testing coverage
- Add performance optimizations
Would you like me to enhance any other package specifications?
Development Guidelines
1. Getting Started
Before implementing pipeline features:
- Review Getting Started Guide
- Check Laravel Compatibility Roadmap
- Follow Testing Guide
- Use Foundation Integration Guide
- Understand Contracts Package Specification
2. Implementation Process
For each pipeline feature:
- Write tests following Testing Guide
- Implement following Laravel patterns
- Document following Getting Started Guide
- Integrate following Foundation Integration Guide
3. Quality Requirements
All implementations must:
- Pass all tests (see Testing Guide)
- Meet Laravel compatibility requirements
- Follow integration patterns (see Foundation Integration Guide)
- Implement required contracts (see Contracts Package Specification)
4. Integration Considerations
When implementing pipelines:
- Follow patterns in Foundation Integration Guide
- Ensure Laravel compatibility per Laravel Compatibility Roadmap
- Use testing approaches from Testing Guide
- Follow development setup in Getting Started Guide
- Implement all contracts from Contracts Package Specification
5. Performance Guidelines
Pipeline system must:
- Handle nested pipelines efficiently
- Minimize memory usage in long pipelines
- Support async operations
- Scale with number of stages
- Meet performance targets in Laravel Compatibility Roadmap
6. Testing Requirements
Pipeline tests must:
- Cover all pipeline types
- Test stage ordering
- Verify error handling
- Check conditional execution
- Follow patterns in Testing Guide
7. Documentation Requirements
Pipeline documentation must:
- Explain pipeline patterns
- Show integration examples
- Cover error handling
- Include performance tips
- Follow standards in Getting Started Guide