harvest 2.0.13

  • README.md
  • CHANGELOG.md
  • Installing
  • Versions
  • 30

Build Status Coverage Status

Harvest

Harvest is a messagebus, CQRS framework and eventstore for Dart with multiple backends. Features include

Message bus features

  • Synchronous and asynchronous delivery
  • Support for both "fire-and-forget" as well as delivery notication
  • Custom message callsbacks allowing subscribers to notify publishers of message status
  • Simple message bus API including standard Dart Stream/Sink interface
  • Extensive error handling support
  • Intercept dead messages (i.e. messages with no listeners)
  • Listen to all published events regardless of type
  • Enrich messages prior to their delivery

Event store features

  • Create, persist and manipulate streams of events independently of the event storage type
  • Simple API for supporting differnt storage backends for events
  • Supports file, IndexedDB and memory backends

CQRS support

  • Support for creating event sourced entities and aggregate roots
  • Support SAGA patterns for long running business process including compensating actions

MessageBus

You can use the message bus standalone or in conjuction with the event store. Message delivery can be synchronous or asynchronous

// Synchronous messagebus
var syncMessageBus = new MessageBus()

// Asynchronous messagebus
var asyncmessageBus = new MessageBus.async()

the message bus support both "fire-and-forget" as well as delivery notications

// subscribe to MyMessage
messageBus.subscribe(MyMessage, (MyMessage msg) => print(msg));

// Publish message and continue immediately
messageBus.publish(myMessage);

// Publish message and await until it has been handled by all subscribers  
int deliveredTo = await messageBus.publish(myMessage);

Messages can be programmatically completed by subscribers by using the CallbackCompleted message mixin

// Message that is completed by custom callback
class CallbackMessage extends Message with CallbackCompleted {
}
// CallbackCompleted messages provide their own success callback
messageBus.subscribe(CallbackMessage, (CallbackMessage msg) {
  print("handled $msg");
  // complete callback successfully
  msg.completed(true, "callback data");
});

// Publish message the result will contain "callback data"
var result = await messageBus.publish(myMessage);

// CallbackCompleted messages provide their own error callback
messageBus.subscribe(CallbackMessage, (CallbackMessage msg) {
  print("handled $msg");
  // complete message with an error
  msg.completed(false, "some error occured");
});

try {
  messageBus.publish(new CallbackMessage();
} catch(e} {
  // e = "some error occured";
}

If you prefer the Dart Stream/Sink interface HArvest supports this as well

// Get a stream for MyMessage
var stream = messageBus.stream(MyMessage);
// Listen to stream
stream.listen((MyMessage myMessage) => print("recieved message $myMessage"));

// Get sink for MyMessage
var sink = messageBus.sink(MyMessage);
// Use sink to dispatch event
sink.add(new MyMessage("a message"));

Error handling can be done both through streams and message bus interface

// Using stream interface: **onError** function invoked when listener fails
messageBus.stream(MyMessage).listen((m) {
  // handled m
}, onError:(e) => print("error $e"));

// Using messagebus interface: **onError** function invoked when listener fails
messageBus.subscribe(MyMessage, (m) {
  // handled m
}, onError:(e) => print("error $e"));


Harvest exposes a hook for intercepting messages that has no listeners

// Handler invoked when a message with no subscribers is published
messageBus.deadMessageHandler = (Message msg) => print("no handler for ${msg.runtimeType}");

You can also listen to all events regardless of types

messageBus.everyMessage.listen((Message msg) => print("message ${msg.runtimeType} published");

If you want to intercept all messages prior to delivery you can use Harvest message enricher API

// Enricher that stores the current user id in the messages header
messageBus.enricher = (Message m) {
  m.headers["userId"] = StaticSessionData.userId;    
};  

Event store

Harvest event store can be used for simply storing events which can later be retrieved

	import 'package:harvest/harvest.dart';

	main() async {
		var streamId = new Guid();
		var eventStore = new MemoryEventStore();
		// get a event stream for streamId
		eventStream = await eventStore.openStream(streamId);
		// create some events
		var event1 = ...
		var event2 = ...

		// store them
		eventStream.addAll([event1, event2]);
		eventStream.commitChanges();
	}

Various backends are included in the standard Harvest distribution

  // Harvest FileEventStore that stores events in JSON files
  import 'package:harvest/harvest_file.dart';
  var eventStore = new FileEventStore("path/to/file.txt");

  // Harvest IdbEventStore that stores events in IndexdDB databases
  import 'package:harvest/harvest_idb.dart';
  var eventStore = new IdbEventStore("idb_database");

new EventStores can be created by implementing the EventStore and EventStream APIs

Event sourcing

Fully fletched event sourced applications are also supported. Event sourcing is the concept of saving and retriving objects by the events that occured on them rather than by their state. Consider the following bank account use case:

  1. User creates account
  2. User deposits 10$
  3. User withdraws 2$

In a CRUD application you would now have a BankAccount object with an amount property with the value 8. In a event sourced application you have a BankAccount object and 3 events for it

  1. AccountCreated
  2. AmountDeposited
  3. AmountWithdrawn

Where is this useful?

  • For certain applications the eventlog can be useful in itself such as a audit trail in a financial system.
  • It can help manage complexity in large applications by forcing programmers to make event types for every action that can occur.
  • It makes debugging easy since you can replay the event log to recreate any former system state where an error occurred.
  • It makes mobile app synchronization a breeze, since the offline app can just queue up events and replay them on the backend once it comes online.
  • In applications using the CQRS architecture pattern.

For more information, see the provided example application.

  • https://github.com/NEventStore/NEventStore

TODO

  • Rewrite example GUI app in Polymer
  • Create serialization interface that allows switching between mirror, transformation and manual serializations
  • Document usage of SAGAs (process manager)
  • Ensure message headers are not serialized
  • Enable CQRS event reloading test
  • Create serialization interface that allows switching between mirror, transformation and manual serializations

2.0.11

  • Minor improvements to tests

2.0.10

  • Enable GUI tests of IndexedDB using content shell

2.0.9

  • Bump polymer dependencies

2.0.8

  • Fix changelog

2.0.8

  • Fix changelog

2.0.7

  • Correct coverage status URL

2.0.6

  • Publish new version as pub upload of 2.0.5 failed

2.0.5

  • MessageBus publish return type changed to Future to signify that error and callback handling may return different data
  • MessageBus Dart Stream interface now also supports CallbackCompleted messages
  • CQRS add saveAggregate and emit methods to the DomainRepository for fine-grained controll when saving aggregates as event streams.
  • CQRS change error handling semantics to better allow SAGA failure compensation

2.0.4

  • Messagebus now correctly propagates errors in message handlers
  • MessageBus supports cancelOnError flag and onError callback
  • Moved DomainCommands support for handler callback in to CallbackCompleted mixin allowing all message types to provide handler callbacks
  • Updated documentation

2.0.0

  • MessageBus now supports both synchronous and asynchronous delivery
  • MessageBus fire method renamed to publish and listen to subscribe
  • MessageBus publish method now returns correct number of message deliveries
  • CQRS module now includes basic SAGA process support including compensating actions

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  harvest: "^2.0.13"

2. Install it

You can install packages from the command line:

with pub:


$ pub get

Alternatively, your editor might support pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:harvest/harvest.dart';
        
Version Uploaded Documentation Archive
2.0.13 Apr 19, 2016 Go to the documentation of harvest 2.0.13 Download harvest 2.0.13 archive
2.0.12 Feb 19, 2016 Go to the documentation of harvest 2.0.12 Download harvest 2.0.12 archive
2.0.11 Feb 1, 2016 Go to the documentation of harvest 2.0.11 Download harvest 2.0.11 archive
2.0.10 Feb 1, 2016 Go to the documentation of harvest 2.0.10 Download harvest 2.0.10 archive
2.0.9 Nov 10, 2015 Go to the documentation of harvest 2.0.9 Download harvest 2.0.9 archive
2.0.8 Aug 30, 2015 Go to the documentation of harvest 2.0.8 Download harvest 2.0.8 archive
2.0.7 Aug 30, 2015 Go to the documentation of harvest 2.0.7 Download harvest 2.0.7 archive
2.0.6 Aug 30, 2015 Go to the documentation of harvest 2.0.6 Download harvest 2.0.6 archive
2.0.5 Aug 30, 2015 Go to the documentation of harvest 2.0.5 Download harvest 2.0.5 archive
2.0.4 Aug 25, 2015 Go to the documentation of harvest 2.0.4 Download harvest 2.0.4 archive

All 40 versions...

Analysis

This feature is new.
We welcome feedback.
More details: scoring.

We analyzed this package, and provided a score, details, and suggestions below.

  • tool failures on Feb 16, 2018
  • Dart: 2.0.0-dev.20.0
  • pana: 0.10.1

Scores

Popularity:
Describes how popular the package is relative to other packages. [more]
0 / 100
Health:
Code health derived from static analysis. [more]
91 / 100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
14 / 100
Overall score:
Weighted score of the above. [more]
30

Platforms

Detected platforms: web, other

Primary library: package:harvest/harvest.dart with components: mirrors.

Suggestions

  • Fix lib/src/message_bus.dart.

    Strong-mode analysis of lib/src/message_bus.dart failed with the following error:

    line: 458 col: 3
    Invalid override. The type of 'MessageStreamSubscription.asFuture' ('([dynamic]) → Future<dynamic>') isn't a subtype of 'StreamSubscription<Message>.asFuture' ('<E>([E]) → Future<E>').

  • Fix lib/src/cqrs/aggregate_root.dart.

    Strong-mode analysis of lib/src/cqrs/aggregate_root.dart failed with the following error:

    line: 47 col: 3
    Invalid override. The type of 'AggregateRoot.==' ('(AggregateRoot) → bool') isn't a subtype of 'Object.==' ('(dynamic) → bool').

  • Fix further 1 Dart files.

    Similar analysis of the following files failed:

    • lib/src/cqrs/domain_repository.dart
  • Fix issues reported by dartanalyzer.

    dartanalyzer reported 3 error(s) and 0 warning(s).

  • Maintain an example.

    None of the files in your example/ directory matches a known example patterns. Common file name patterns include: main.dart, example.dart or you could also use harvest.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.9.0 <2.0.0
log4dart ^1.4.19 1.4.19
serialization ^0.10.4+4 0.10.4+4
uuid ^0.5.2 0.5.3
Transitive dependencies
analyzer 0.27.6 0.31.1
args 0.13.7 1.3.0
async 2.0.3 2.0.4
barback 0.15.2+14
charcode 1.1.1
collection 1.14.5
convert 2.0.1
crypto 2.0.2+1
csslib 0.14.1
glob 1.1.5
html 0.13.2+2
isolate 0.2.3 1.1.0
logging 0.11.3+1
package_config 1.0.3
path 1.5.1
plugin 0.2.0+2
pool 1.3.4
source_span 1.4.0
sprintf 1.0.8 3.0.2
stack_trace 1.9.2
string_scanner 1.0.2
typed_data 1.1.5
utf 0.9.0+4
watcher 0.9.7+7
yaml 2.1.13
Dev dependencies
polymer ^0.16.4+1
route_hierarchical ^0.6.2
test any