Skip to content

A simple yet highly configurable Mediator implementation that allows sending requests and publishing events.

License

Notifications You must be signed in to change notification settings

MatthiWare/mediator.dart

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mediator.dart

Dart pub package codecov

Description

A Mediator implementation for Dart inspired by MediatR.

This package provides a simple yet configurable solution.

Features

  • Request/Response
  • Commands
  • Request/Command Pipelines
  • Events
  • Event Observers

Sending events

An event can have multiple handlers. All handlers will be executed in parallel (by default).

import 'package:dart_mediator/mediator.dart';

/// Strongly typed event class containing the event data.
/// All events must implement the [DomainEvent] interface.
class MyEvent implements DomainEvent {}

Future<void> main() async {
  final mediator = Mediator.create();

  // Subscribe to the event.
  mediator.events.on<MyEvent>()
    .subscribeFunction(
      (event) => print('event received'),
    );

  // Sends the event to all handlers.
  // This will print 'event received'.
  await mediator.events.dispatch(MyEvent());
}

Sending Commands

A command can only have one handler and doesn't return a value.

/// This command will not return a value.
class MyCommand implements Command {}

class MyCommandHandler implements CommandHandler<MyCommand> {
  @override
  FutureOr<void> handle(MyCommand request) {
    // Do something
  }
}

Future<void> main() async {
  final mediator = Mediator.create();

  mediator.requests.register(MyCommandHandler());

  /// Sends the command request. Return value is [void].
  await mediator.requests.send(MyCommand());
}

Sending Requests

A request can only have one handler and returns a value.

import 'package:dart_mediator/mediator.dart';

class Something {}

/// This query will return a [Something] object.
class MyQuery implements Query<Something> {}

class MyQueryHandler implements QueryHandler<Something, MyQuery> {
  @override
  FutureOr<Something> handle(MyQuery request) {
    // do something
    return Something();
  }
}

Future<void> main() async {
  final mediator = Mediator.create();

  mediator.requests.register(MyQueryHandler());

  // Sends the query request and returns the response.
  final Something response = await mediator.requests.send(MyQuery());

  print(response);
}

Event Observers

An observer can be used to observe events being dispatched, handled or when an error occurs. For example logging events.

class LoggingEventObserver implements EventObserver {

  /// Called when an event is dispatched but before any handlers have
  /// been called.
  @override
  void onDispatch<TEvent extends DomainEvent>(
    TEvent event,
    Set<EventHandler<TEvent>> handlers,
  ) {
    print(
      '[LoggingEventObserver] onDispatch "$event" with ${handlers.length} handlers',
    );
  }

  /// Called when an event returned an error for a given handler.
  @override
  void onError<TEvent extends DomainEvent>(
    TEvent event,
    EventHandler<TEvent> handler,
    Object error,
    StackTrace stackTrace,
  ) {
    print('[LoggingEventObserver] onError $event -> $handler ($error)');
  }

  /// Called when an event has been handled by a handler.
  @override
  void onHandled<TEvent extends DomainEvent>(
    TEvent event,
    EventHandler<TEvent> handler,
  ) {
    print('[LoggingEventObserver] onHandled $event -> $handler');
  }
}

void main() {
  final mediator = Mediator.create(
    // Adds the logging event observer.
    observers: [LoggingEventObserver()],
  );

  // Dispatch an event.
}

Request/Command Pipeline Behavior

A pipeline behavior can be used to add cross cutting concerns to requests/commands. For example logging.

class LoggingBehavior implements PipelineBehavior {
  @override
  Future handle(request, RequestHandlerDelegate next) async {
    try {
      print('[$LoggingBehavior] [${request.runtimeType}] Before');
      return await next();
    } finally {
      print('[$LoggingBehavior] [${request.runtimeType}] After');
    }
  }
}

void main() {
    final mediator = Mediator.create();

    // add logging behavior
    mediator.requests.pipeline.registerGeneric(LoggingBehavior());
}

Credits