Skip to content
/ hermes Public

EventStoreDB-like stream projections on top of Apachi Kafka.

License

Notifications You must be signed in to change notification settings

ostafen/hermes

Repository files navigation

Hermes

Hermes implements the EventStoreDB projection subsystem on top of Apachi Kafka. It is meant to simplify the development of architectures based on CQRS and event-sourcing.

Why Hermes?

EventStoreDB projections have been originally designed to process large streams of events in a reactive manner. They can solve almost any business problem where near real-time complex event processing is required (for example, monitoring of temperature sensors, reacting to changes in the stock market, etc...). Kakfa is a de-facto standard when it comes to stream processing, and ships with stateful processing facilities, fault tolerance and processing guarantees. Its partitioned layout makes it suitable for processing thousands of events in a reliable manner.

Build (Go 1.9+)

Run the following command

foo@bar$ make build

Local service setup

logging:
  level: INFO
  format: JSON

server:
  port: 9175 # http service port

kafka:
  brokers: 
    - "localhost:9092"

To start the service, run the command:

foo@bar$ ./bin/hermes config.yml

Docker-compose setup

---
version: '3'
services:
  zookeper:
    ...
  
  kafka:
    ...

  hermes:
    image: ghcr.io/ostafen/hermes:0.0.1-alpha
    ports:
      - '9175:9175'
    environment:
      SERVER_PORT: 9175
      KAFKA_BROKERS: localhost:9092

You can find a ready to use docker compose file here.

Projections

Projections are defined in Javascript, through a powerful and flexible state transformation API. For example, the following snippet of code defines a projection which computes the total number of events having a specific eventType.

fromStream('my-stream').
    partitionBy(e => e.eventType).
    when({
        $init: function() {
            return { count: 0 }
        },
        $any: function(state, e) {
            state.count += 1
        }
    }).
    filterBy(s => s.count >= 10).
    transformBy(function(state) {
        return { Total: state.count }
    }).
    outputTo('out-stream')

Assuming the projection is stored in a file named projection.js, you can use the following command to bootstrap the projection:

curl -X POST localhost:9175/projections/my-projection -H 'Content-Type: text/javascript' --data-binary @projection.js

Supported Projections Operators

Selectors

Selector Description
fromStream({streamId}) Selects events from the streamId stream.
fromStreams() Selects events from the streams supplied.

Filters and Transformations

Selector Description Provides
when(handlers) Allows only the given events of a particular to pass through the projection. transformBy, filterBy, outputTo, outputState
outputState() If the projection is statefull, setting this option produces a stream called projections-{projection-name}-result with the state as the event body. transformBy, filterBy, outputTo
partitionBy(function(event)) Partitions a projection by the partition returned from the handler. transformBy, filterBy, outputTo
transformBy(function(state)) Provides the ability to transform the state of a projection by the provided handler. transformBy, filterBy, outputTo, outputState
filterBy(function(state)) Causes projection results to be null for any state that returns a false value from the given predicate. transformBy, filterBy, outputTo, outputState

REST API

  • POST /projections/{name} - Create a new projections
  • DELETE /projections/{name} - Delete an existing projections

Contact

Stefano Scafiti @ostafen

License

Hermes source code is available under the MIT License.

Contributing

Hermes is actively developed. If you want to contribute to the project, please read the following contribution guidelines.