platform/doc/bus_package_specification.md
2024-11-25 20:33:45 -07:00

424 lines
10 KiB
Markdown

# Bus Package Specification
## Overview
The Bus package provides a robust command and event bus implementation that matches Laravel's bus functionality. It integrates with our Queue, Event, and Pipeline packages to provide a complete message bus solution with support for command handling, event dispatching, and middleware processing.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Events Package Specification](events_package_specification.md) for event handling
> - See [Queue Package Specification](queue_package_specification.md) for queue integration
## Core Features
### 1. Command Bus
```dart
/// Core command bus implementation
class CommandBus implements CommandBusContract {
final Container _container;
final QueueContract? _queue;
final PipelineContract _pipeline;
CommandBus(
this._container,
this._pipeline, [
this._queue
]);
/// Dispatches a command
Future<dynamic> dispatch(Command command) async {
if (command is ShouldQueue && _queue != null) {
return await dispatchToQueue(command);
}
return await dispatchNow(command);
}
/// Dispatches a command immediately
Future<dynamic> dispatchNow(Command command) async {
var handler = _resolveHandler(command);
return await _pipeline
.send(command)
.through(_getPipes())
.then((cmd) => handler.handle(cmd));
}
/// Dispatches a command to queue
Future<dynamic> dispatchToQueue(Command command) async {
await _queue!.push(QueuedCommandJob(
command: command,
handler: _resolveHandler(command)
));
}
/// Creates a command batch
PendingBatch batch(List<Command> commands) {
return PendingCommandBatch(this, commands);
}
/// Creates a command chain
PendingChain chain(List<Command> commands) {
return PendingCommandChain(this, commands);
}
/// Resolves command handler
Handler _resolveHandler(Command command) {
var handlerType = command.handler;
return _container.make(handlerType);
}
/// Gets command pipes
List<PipeContract> _getPipes() {
return [
TransactionPipe(),
ValidationPipe(),
AuthorizationPipe()
];
}
}
```
### 2. Event Bus
```dart
/// Core event bus implementation
class EventBus implements EventBusContract {
final Container _container;
final EventDispatcherContract _events;
final QueueContract? _queue;
EventBus(
this._container,
this._events, [
this._queue
]);
/// Dispatches an event
Future<void> dispatch(Event event) async {
if (event is ShouldQueue && _queue != null) {
await dispatchToQueue(event);
} else {
await dispatchNow(event);
}
}
/// Dispatches an event immediately
Future<void> dispatchNow(Event event) async {
await _events.dispatch(event);
}
/// Dispatches an event to queue
Future<void> dispatchToQueue(Event event) async {
await _queue!.push(QueuedEventJob(
event: event,
listeners: _events.getListeners(event.runtimeType)
));
}
/// Registers an event listener
void listen<T>(void Function(T event) listener) {
_events.listen<T>(listener);
}
/// Registers an event subscriber
void subscribe(EventSubscriber subscriber) {
_events.subscribe(subscriber);
}
}
```
### 3. Bus Middleware
```dart
/// Transaction middleware
class TransactionPipe implements PipeContract {
final DatabaseManager _db;
TransactionPipe(this._db);
@override
Future<dynamic> handle(dynamic passable, Function next) async {
return await _db.transaction((tx) async {
return await next(passable);
});
}
}
/// Validation middleware
class ValidationPipe implements PipeContract {
final Validator _validator;
ValidationPipe(this._validator);
@override
Future<dynamic> handle(dynamic passable, Function next) async {
if (passable is ValidatableCommand) {
await _validator.validate(
passable.toMap(),
passable.rules()
);
}
return await next(passable);
}
}
/// Authorization middleware
class AuthorizationPipe implements PipeContract {
final AuthManager _auth;
AuthorizationPipe(this._auth);
@override
Future<dynamic> handle(dynamic passable, Function next) async {
if (passable is AuthorizableCommand) {
if (!await passable.authorize(_auth)) {
throw UnauthorizedException();
}
}
return await next(passable);
}
}
```
### 4. Command Batching
```dart
/// Pending command batch
class PendingCommandBatch implements PendingBatch {
final CommandBus _bus;
final List<Command> _commands;
bool _allowFailures = false;
PendingCommandBatch(this._bus, this._commands);
/// Allows failures in batch
PendingBatch allowFailures() {
_allowFailures = true;
return this;
}
/// Dispatches the batch
Future<void> dispatch() async {
for (var command in _commands) {
try {
await _bus.dispatchNow(command);
} catch (e) {
if (!_allowFailures) rethrow;
}
}
}
}
/// Pending command chain
class PendingCommandChain implements PendingChain {
final CommandBus _bus;
final List<Command> _commands;
PendingCommandChain(this._bus, this._commands);
/// Dispatches the chain
Future<void> dispatch() async {
dynamic result;
for (var command in _commands) {
if (command is ChainedCommand) {
command.setPreviousResult(result);
}
result = await _bus.dispatchNow(command);
}
}
}
```
## Integration Examples
### 1. Command Bus Usage
```dart
// Define command
class CreateOrder implements Command {
final String customerId;
final List<String> products;
@override
Type get handler => CreateOrderHandler;
}
// Define handler
class CreateOrderHandler implements Handler<CreateOrder> {
final OrderRepository _orders;
CreateOrderHandler(this._orders);
@override
Future<Order> handle(CreateOrder command) async {
return await _orders.create(
customerId: command.customerId,
products: command.products
);
}
}
// Dispatch command
var order = await bus.dispatch(CreateOrder(
customerId: '123',
products: ['abc', 'xyz']
));
```
### 2. Event Bus Usage
```dart
// Define event
class OrderCreated implements Event {
final Order order;
OrderCreated(this.order);
}
// Register listener
eventBus.listen<OrderCreated>((event) async {
await notifyCustomer(event.order);
});
// Dispatch event
await eventBus.dispatch(OrderCreated(order));
```
### 3. Command Batching
```dart
// Create batch
await bus.batch([
CreateOrder(...),
UpdateInventory(...),
NotifyShipping(...)
])
.allowFailures()
.dispatch();
// Create chain
await bus.chain([
CreateOrder(...),
ProcessPayment(...),
ShipOrder(...)
])
.dispatch();
```
## Testing
```dart
void main() {
group('Command Bus', () {
test('dispatches commands', () async {
var bus = CommandBus(container, pipeline);
var command = CreateOrder(...);
await bus.dispatch(command);
verify(() => handler.handle(command)).called(1);
});
test('handles command batch', () async {
var bus = CommandBus(container, pipeline);
await bus.batch([
CreateOrder(...),
UpdateInventory(...)
]).dispatch();
verify(() => bus.dispatchNow(any())).called(2);
});
});
group('Event Bus', () {
test('dispatches events', () async {
var bus = EventBus(container, dispatcher);
var event = OrderCreated(order);
await bus.dispatch(event);
verify(() => dispatcher.dispatch(event)).called(1);
});
test('queues events', () async {
var bus = EventBus(container, dispatcher, queue);
var event = OrderShipped(order);
await bus.dispatch(event);
verify(() => queue.push(any())).called(1);
});
});
}
```
## Next Steps
1. Implement core bus features
2. Add middleware support
3. Add batching/chaining
4. Add queue integration
5. Write tests
6. Add benchmarks
## Development Guidelines
### 1. Getting Started
Before implementing bus features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Events Package Specification](events_package_specification.md)
6. Review [Queue Package Specification](queue_package_specification.md)
### 2. Implementation Process
For each bus feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in related packages
### 4. Integration Considerations
When implementing bus features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Bus system must:
1. Handle high command throughput
2. Process events efficiently
3. Support async operations
4. Scale horizontally
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Bus tests must:
1. Cover all command scenarios
2. Test event handling
3. Verify queue integration
4. Check middleware behavior
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Bus documentation must:
1. Explain command patterns
2. Show event examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)