raiser 2.0.1
raiser: ^2.0.1 copied to clipboard
A type-safe, async-first domain event library for Dart. Clean event bus implementation following clean architecture principles.
Raiser #
[Raiser Logo]
A type-safe, async-first domain event library for Dart. Raiser provides a clean event bus implementation following clean architecture principles, perfect for decoupling components in your application.
Features #
- Type-safe event handling — Generic handlers ensure compile-time type checking
- Async-first design — All handlers are asynchronous by default
- Priority-based ordering — Control handler execution order with priorities
- Middleware support — Wrap handler execution with cross-cutting concerns
- Flexible error strategies — Choose how errors are handled during event propagation
- Subscription management — Cancel handlers when no longer needed
- Domain event metadata — Built-in support for event IDs, timestamps, and aggregate IDs
- DDD-friendly — Designed with Domain-Driven Design patterns in mind
Installation #
dependencies:
raiser: ^1.0.0
Quick Start #
import 'package:raiser/raiser.dart';
// Define an event
class UserCreated extends RaiserEvent {
final String userId;
final String email;
UserCreated({required this.userId, required this.email});
@override
Map<String, dynamic> toMetadataMap() => {
...super.toMetadataMap(),
'userId': userId,
'email': email,
};
}
void main() async {
final bus = EventBus();
// Subscribe to events
bus.on<UserCreated>((event) async {
print('Welcome ${event.email}!');
});
// Publish events
await bus.publish(UserCreated(userId: '123', email: 'alice@example.com'));
}
Domain Events #
All events extend RaiserEvent, which provides automatic metadata:
| Property | Description |
|---|---|
id |
Unique identifier (auto-generated) |
timestamp |
Creation time (auto-captured) |
aggregateId |
Optional link to a domain aggregate |
class OrderPlaced extends RaiserEvent {
final String orderId;
final double amount;
OrderPlaced({required this.orderId, required this.amount, super.aggregateId});
@override
Map<String, dynamic> toMetadataMap() => {
...super.toMetadataMap(),
'orderId': orderId,
'amount': amount,
};
}
Event Handlers #
Function Handlers #
Quick inline handlers:
bus.on<UserCreated>((event) async {
await sendWelcomeEmail(event.email);
});
Class-Based Handlers #
Better for complex logic and testing:
class WelcomeEmailHandler implements EventHandler<UserCreated> {
@override
Future<void> handle(UserCreated event) async {
await sendWelcomeEmail(event.email);
}
}
bus.register<UserCreated>(WelcomeEmailHandler());
Handler Priority #
Higher values execute first:
bus.on<OrderPlaced>((e) async => print('Second'), priority: 0);
bus.on<OrderPlaced>((e) async => print('First'), priority: 10);
bus.on<OrderPlaced>((e) async => print('Last'), priority: -5);
Middleware #
Wrap handler execution with cross-cutting concerns like logging, timing, or validation:
// Add middleware that wraps all handler execution
bus.addMiddleware((event, next) async {
print('Before: ${event.runtimeType}');
await next();
print('After: ${event.runtimeType}');
}, priority: 100);
// Middleware with higher priority wraps those with lower priority
bus.addMiddleware((event, next) async {
final stopwatch = Stopwatch()..start();
await next();
print('Took ${stopwatch.elapsedMilliseconds}ms');
}, priority: 50);
Subscriptions #
Both on(), register(), and addMiddleware() return a Subscription:
final subscription = bus.on<UserCreated>((event) async {
// Handle event
});
// Stop receiving events
subscription.cancel();
// Check status
print(subscription.isCancelled); // true
Error Handling #
Configure error behavior with ErrorStrategy:
| Strategy | Behavior |
|---|---|
stop |
Halt on first error, rethrow immediately (default) |
continueOnError |
Run all handlers, throw AggregateException |
swallow |
Run all handlers, errors only go to callback |
// Stop on first error (default)
final bus = EventBus(errorStrategy: ErrorStrategy.stop);
// Collect all errors
final bus = EventBus(errorStrategy: ErrorStrategy.continueOnError);
// Silent failures with logging
final bus = EventBus(
errorStrategy: ErrorStrategy.swallow,
onError: (error, stackTrace) => logger.error('Failed: $error'),
);
AggregateException #
When using continueOnError:
try {
await bus.publish(event);
} on AggregateException catch (e) {
print('${e.errors.length} handlers failed');
}
Code Generation #
For automatic handler discovery and registration, use the companion packages:
dependencies:
raiser: ^1.0.0
raiser_annotation: ^1.0.0
dev_dependencies:
build_runner: ^2.4.0
raiser_generator: ^1.0.0
See raiser_generator for details.
License #
MIT License - see LICENSE for details.