gossip_typed_events library

Type-safe event extensions for the Dart gossip protocol library.

This library provides strongly-typed event capabilities on top of the core gossip protocol, enabling developers to work with type-safe events while maintaining full compatibility with the underlying gossip system.

Features

  • 🎯 Type-safe events - Define custom event types with compile-time safety
  • 📝 Automatic serialization - Events are automatically serialized/deserialized
  • 🔍 Event filtering - Stream-based filtering by event type
  • 📊 Event registry - Central registry for managing event types
  • 🔄 Stream transformers - Powerful stream processing capabilities
  • 🛡️ Validation support - Built-in validation framework for events
  • 📦 Metadata support - Attach metadata to events for additional context

Quick Start

1. Define Your Event Types

class UserLoginEvent extends TypedEvent {
  final String userId;
  final DateTime timestamp;

  UserLoginEvent({required this.userId, required this.timestamp});

  @override
  String get type => 'user_login';

  @override
  Map<String, dynamic> toJson() => {
    'userId': userId,
    'timestamp': timestamp.millisecondsSinceEpoch,
  };

  factory UserLoginEvent.fromJson(Map<String, dynamic> json) {
    return UserLoginEvent(
      userId: json['userId'] as String,
      timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
    );
  }
}

2. Register Event Types

void main() {
  final registry = TypedEventRegistry();

  registry.register<UserLoginEvent>(
    'user_login',
    (json) => UserLoginEvent.fromJson(json),
  );

  // ... rest of your application
}

3. Use Typed Events with GossipNode

import 'package:gossip/gossip.dart';
import 'package:gossip_typed_events/gossip_typed_events.dart';

// Create and broadcast typed events
final loginEvent = UserLoginEvent(
  userId: 'user123',
  timestamp: DateTime.now(),
);

await node.createTypedEvent(loginEvent);

// Listen for typed events
node.onTypedEvent<UserLoginEvent>((json) => UserLoginEvent.fromJson(json))
    .listen((event) {
  print('User ${event.userId} logged in at ${event.timestamp}');
});

// Or use the registry for automatic deserialization
node.onRegisteredTypedEvent<UserLoginEvent>().listen((event) {
  print('User ${event.userId} logged in');
});

Advanced Usage

Event Validation

class OrderEvent extends TypedEvent with TypedEventMixin {
  final String orderId;
  final double amount;

  OrderEvent({required this.orderId, required this.amount});

  @override
  String get type => 'order_created';

  @override
  void validate() {
    super.validate();
    if (orderId.isEmpty) throw ArgumentError('orderId cannot be empty');
    if (amount <= 0) throw ArgumentError('amount must be positive');
  }

  @override
  Map<String, dynamic> toJson() => {
    ...toJsonWithMetadata(),
    'orderId': orderId,
    'amount': amount,
  };
}

Stream Transformers

// Transform raw events to typed events
final typedStream = rawEventStream.transform(
  typedEventTransformer<UserLoginEvent>(
    eventType: 'user_login',
    factory: (json) => UserLoginEvent.fromJson(json),
  ),
);

// Multi-type transformer
final multiStream = rawEventStream.transform(
  multiTypeEventTransformer(
    includeTypes: {'user_login', 'user_logout', 'order_created'},
  ),
);

Metadata and Timestamps

class EnhancedEvent extends TypedEvent with TypedEventMixin {
  EnhancedEvent() {
    setMetadata('source', 'mobile_app');
    setMetadata('version', '1.2.3');
  }

  @override
  String get type => 'enhanced_event';

  @override
  Map<String, dynamic> toJson() => {
    ...toJsonWithMetadata(), // Includes createdAt and metadata
    'data': 'some_data',
  };
}

Architecture

This library extends the core gossip protocol with type safety while maintaining full backward compatibility:

┌─────────────────────────────────────────┐
│           Your Application              │
├─────────────────────────────────────────┤
│      Typed Events (this library)       │
│  ┌─────────────┐  ┌─────────────────┐   │
│  │ TypedEvent  │  │ TypedGossipNode │   │
│  └─────────────┘  └─────────────────┘   │
├─────────────────────────────────────────┤
│         Core Gossip Protocol            │
│  ┌─────────────┐  ┌─────────────────┐   │
│  │    Event    │  │   GossipNode    │   │
│  └─────────────┘  └─────────────────┘   │
└─────────────────────────────────────────┘

Best Practices

  • Type Consistency: Use consistent type identifiers across your application
  • Validation: Implement validation for critical event types
  • Registry Management: Register all event types at application startup
  • Error Handling: Handle deserialization errors gracefully
  • Versioning: Consider versioning your event schemas for backward compatibility
  • Testing: Test both serialization and deserialization of your events

Performance Considerations

  • Lazy Deserialization: Events are only deserialized when accessed
  • Efficient Filtering: Stream transformers filter at the gossip level
  • Registry Overhead: Minimal overhead from the singleton registry
  • Memory Usage: TypedEvents have similar memory footprint to raw events

Error Handling

All typed event operations provide comprehensive error handling:

try {
  await node.createTypedEvent(event);
} on TypedEventException catch (e) {
  print('Failed to broadcast event: ${e.message}');
} on TypedEventRegistryException catch (e) {
  print('Registry error: ${e.message}');
}

Classes

MultiTypeEventTransformer
Multi-type stream transformer for typed events.
RegistryTypedEventTransformer<T extends TypedEvent>
Stream transformer that uses the TypedEventRegistry for deserialization.
TypedEvent
Base class for typed events.
TypedEventCreated<T extends TypedEvent>
A typed event that was created locally with its original Event metadata.
TypedEventInfo
Information about a typed event without full deserialization.
TypedEventReceived<T extends TypedEvent>
A typed event that was received from a peer with full metadata.
TypedEventRegistry
Registry for typed event factories.
TypedEventRegistryStats
Statistics about the typed event registry.
TypedEventTransformer<T extends TypedEvent>
Stream transformer for typed events.
TypedEventValidatable
Interface for typed events that support validation.
TypedReceivedEvent
A typed event that was received from a peer.

Mixins

TypedEventMixin
Mixin for common typed event functionality.

Extensions

TypedGossipNode on GossipNode
Extension on GossipNode to support typed events.

Functions

multiTypeEventTransformer({bool skipErrors = true, void onError(Event event, Object error, StackTrace stackTrace)?, TypedEventRegistry? registry, Set<String>? includeTypes, Set<String>? excludeTypes}) MultiTypeEventTransformer
Helper function to create a multi-type event transformer.
registryTypedEventTransformer<T extends TypedEvent>({bool skipErrors = true, void onError(Event event, Object error, StackTrace stackTrace)?, TypedEventRegistry? registry}) RegistryTypedEventTransformer<T>
Helper function to create a registry-based typed event transformer.
typedEventTransformer<T extends TypedEvent>({required String eventType, required T factory(Map<String, dynamic>), bool skipErrors = true, void onError(Event event, Object error, StackTrace stackTrace)?}) TypedEventTransformer<T>
Helper function to create a typed event transformer.

Exceptions / Errors

TypedEventException
Exception thrown when typed event operations fail.
TypedEventRegistryException
Exception thrown when typed event registry operations fail.
TypedEventTransformerException
Exception thrown when typed event transformer operations fail.