raiser 3.0.0 copy "raiser: ^3.0.0" to clipboard
raiser: ^3.0.0 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: ^3.0.0

Quick Start #

EventBus works with ANY type - you don't need to use RaiserEvent at all:

import 'package:raiser/raiser.dart';

// Define an event - just a simple class!
final class UserCreated {
  UserCreated({
    required this.userId,
    required this.email,
  });

  final String userId;
  final String 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: '[email protected]'));
}

RaiserEvent (Optional) #

You don't have to use RaiserEvent! The EventBus is fully generic and works with any type.

However, if you want standardized domain event metadata, you can optionally implement RaiserEvent, which is an interface extending ZooperDomainEvent from zooper_flutter_core. This provides:

Property Type Description
id EventId Unique identifier (ULID-based)
occurredOn DateTime When the event occurred
metadata Map<String, Object?> Additional event context

RaiserEvent is intentionally an interface (not a base class) to avoid forcing single inheritance. If you choose to use it, implement it explicitly:

import 'package:zooper_flutter_core/zooper_flutter_core.dart';

final class OrderPlaced implements RaiserEvent {
  OrderPlaced({
    required this.orderId,
    required this.amount,
    EventId? eventId,
    DateTime? occurredOn,
    Map<String, Object?> metadata = const {},
  }) : id = eventId ?? EventId.fromUlid(),
       occurredOn = occurredOn ?? DateTime.now(),
       metadata = Map<String, Object?>.unmodifiable(metadata);

  final String orderId;
  final double amount;

  @override
  final EventId id;

  @override
  final DateTime occurredOn;

  @override
  final Map<String, Object?> metadata;
}

Aggregate IDs #

Store aggregate identifiers in the metadata map:

final event = OrderPlaced(
  orderId: 'order-123',
  amount: 99.99,
  metadata: {'aggregateId': 'user-456'},
);

// Access via metadata
final aggregateId = event.metadata['aggregateId'] as String?;

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((Object event, Future<void> Function() 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((Object event, Future<void> Function() 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: ^3.0.0
  raiser_annotation: ^3.0.0

dev_dependencies:
  build_runner: ^2.4.0
  raiser_generator: ^3.0.0

See raiser_generator for details.

License #

MIT License - see LICENSE for details.

1
likes
160
points
370
downloads

Publisher

verified publisherzooper.dev

Weekly Downloads

A type-safe, async-first domain event library for Dart. Clean event bus implementation following clean architecture principles.

Repository (GitHub)
View/report issues

Topics

#events #event-bus #domain-driven-design #clean-architecture #pub-sub

Documentation

API reference

License

MIT (license)

Dependencies

zooper_flutter_core

More

Packages that depend on raiser