Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: separate readme for each crate #66

Merged
merged 1 commit into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ resolver = "2"
edition = "2021"
authors = [ "Heiko Seeberger <[email protected]>" ]
license = "Apache-2.0"
readme = "README.md"
homepage = "https://github.com/hseeberger/eventsourced"
repository = "https://github.com/hseeberger/eventsourced"

Expand Down
183 changes: 3 additions & 180 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# EventSourced

[![Crates.io][crates-badge]][crates-url]
[![license][license-badge]][license-url]
[![build][build-badge]][build-url]

[crates-badge]: https://img.shields.io/crates/v/eventsourced
[crates-url]: https://crates.io/crates/eventsourced
[license-badge]: https://img.shields.io/github/license/hseeberger/eventsourced
[license-url]: https://github.com/hseeberger/eventsourced/blob/main/LICENSE
[build-badge]: https://img.shields.io/github/actions/workflow/status/hseeberger/eventsourced/ci.yaml
Expand All @@ -15,183 +12,9 @@ Event sourced entities in [Rust](https://www.rust-lang.org/).

## Crates

- `eventsourced`: core library with `EventSourced`, `Entity`, `EvtLog`, `SnapshotStore`, etc.
- `eventsourced-nats`: [NATS](https://nats.io/) implementation for `EvtLog` and `SnapshotStore`
- `eventsourced-postgres`: [Postgres](https://www.postgresql.org/) implementation for `EvtLog` and `SnapshotStore`

## Concepts

EventSourced is inspired to a large degree by the amazing [Akka Persistence](https://doc.akka.io/docs/akka/current/typed/index-persistence.html) library. It provides a framework for implementing [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html) and [CQRS](https://www.martinfowler.com/bliki/CQRS.html).

The `EventSourced` trait defines types for commands, events, snapshot state and errors as well as methods for command handling, event handling and setting a snapshot state.

The `EvtLog` and `SnapshotStore` traits define a pluggable event log and a pluggable snapshot store respectively. For [NATS](https://nats.io/) and [Postgres](https://www.postgresql.org/) these are implemented in the respective crates.

The `spawn` extension method provides for creating entities – "running" instances of an `EventSourced` implementation, identifiable by a `Uuid` – for some event log and some snapshot store. Conversion of events and snapshot state to and from bytes happens via given `binarizer` functions; for [prost](https://github.com/tokio-rs/prost) and [serde_json](https://github.com/serde-rs/json) these are already provided.

Calling `spawn` results in a cloneable `EntityRef` which can be used to pass commands to the spawned entity by invoking `handle_cmd`. Commands are handled by the command handler of the spawned entity. They can be rejected by returning an error. Valid commands produce an event with an optional tag which gets persisted to the `EvtLog` and then applied to the event handler of the respective entity. The event handler may decide to save a snapshot which is used to speed up future spawning.

Events can be queried from the event log by ID or by tag. These queries can be used to build read side projections.

## Requirements for building the project and examples

Before building the project and examples, please make sure you have installed the [protobuf](https://github.com/protocolbuffers/protobuf) dependency that is not only needed for the optional byte conversion with prost, which is a default feature, but also for eventsourced-nats. The only way to get away without `protobuf` is to change the default features and not build eventsourced-nats.

On macOS `protobuf` can be installed via Homebrew:

```
brew install protobuf
```

## Counter example (no pun intended)

The `counter` package in the `example` directory contains a simple example: a counter which handles `Inc` and `Dec` commands and emits/handles `Increased` and `Decreased` events.

```rust
impl EventSourced for Counter {
type Cmd = Cmd;
type Evt = Evt;
type State = u64;
type Error = Error;

/// Command handler, returning the to be persisted event or an error.
fn handle_cmd(&self, cmd: Self::Cmd) -> Result<impl IntoTaggedEvt<Self::Evt>, Self::Error> {
let value = self.value;

match cmd {
Cmd::Inc(inc) if inc > u64::MAX - value => Err(Error::Overflow { value, inc }),
Cmd::Inc(inc) => Ok(Evt::Increased(inc)),

Cmd::Dec(dec) if dec > value => Err(Error::Underflow { value, dec }),
Cmd::Dec(dec) => Ok(Evt::Decreased(dec)),
}
}

/// Event handler, also returning whether to take a snapshot or not.
fn handle_evt(&mut self, evt: Self::Evt) -> Option<Self::State> {
match evt {
Evt::Increased(inc) => self.value += inc,
Evt::Decreased(dec) => self.value -= dec,
}

// No snapshots.
None
}

fn set_state(&mut self, _state: Self::State) {
// This method cannot be called as long as `handle_evt` always returns `None`.
panic!("no snapshots");
}
}
```

There are also the two `counter-nats` and `counter-postgres` packages, with a binary crate each, using `eventsourced-nats` and `eventsourced-postgres` respectively for the event log.

```rust
...
let evt_log = evt_log.clone();
let snapshot_store = snapshot_store.clone();
let counter = Counter::default();
let counter = counter
.spawn(
id,
unsafe { NonZeroUsize::new_unchecked(42) },
evt_log,
snapshot_store,
convert::serde_json::binarizer(),
)
.await
.context("cannot spawn entity")?;

tasks.spawn(async move {
for n in 0..config.evt_count / 2 {
if n > 0 && n % 2_500 == 0 {
println!("{id}: {} events persisted", n * 2);
}
counter
.handle_cmd(Cmd::Inc(n as u64))
.await
.context("cannot handle Inc command")
.unwrap()
.context("invalid command")
.unwrap();
...
}
});
...
```

Take a look at the [examples](examples) directory for more details.

### Running the counter-nats example

For the `counter-nats` example, nats-server needs to be installed. On macOS just use Homebrew:

```
brew install nats-server
```

Before running the example, start the nats-server with the `jetstream` feature enabled:

```
nats-server --jetstream
```

Then use the following command to run the example:

```
RUST_LOG=info \
CONFIG_DIR=examples/counter-nats/config \
cargo run \
--release \
--package counter-nats
```

Notice that you can change the configuration either by changing the `defaul.toml` file at `examples/counter-nats/config` or by overriding the configuration settings with environment variables, e.g. `APP__COUNTER__EVT_COUNT=42`:

```
RUST_LOG=info \
APP__COUNTER__EVT_COUNT=42 \
CONFIG_DIR=examples/counter-nats/config \
cargo run \
--release \
--package counter-nats
```

### Running the counter-postgres example

For the `counter-postgres` example, PostgreSQL needs to be installed. On macOS just use Homebrew:

```
brew install postgresql@14
```

Before running the example, start PostgreSQL:

```
brew services run postgresql@14
```

Make sure you know the following connection parameters:
- host
- port
- user
- password
- dbname

Change the configuration either by changing the `default.toml` file at `examples/counter-postgres/config` or by overriding the configuration settings with environment variables, e.g. `APP__EVT_LOG__DBNAME=test` or `APP__COUNTER__EVT_COUNT=42`:

Then use the following command to run the example:

```
RUST_LOG=info \
APP__EVT_LOG__DBNAME=test \
APP__COUNTER__EVT_COUNT=42 \
CONFIG_DIR=examples/counter-postgres/config \
cargo run \
--release \
--package counter-postgres
```
- [`eventsourced`](https://github.com/hseeberger/eventsourced/blob/main/eventsourced/README.md): core library with `EventSourced`, `Entity`, `EvtLog`, `SnapshotStore`, etc.
- [`eventsourced-nats`](https://github.com/hseeberger/eventsourced/blob/main/eventsourced-nats/README.md): [NATS](https://nats.io/) implementation for `EvtLog` and `SnapshotStore`
- [`eventsourced-postgres`](https://github.com/hseeberger/eventsourced/blob/main/eventsourced-postgres/README.md): [Postgres](https://www.postgresql.org/) implementation for `EvtLog` and `SnapshotStore`

## License ##

Expand Down
4 changes: 2 additions & 2 deletions eventsourced-nats/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "eventsourced-nats"
description = "NATS implementation for eventsourced EvtLog and SnapshotStore."
description = "NATS implementation for EventSourced EvtLog and SnapshotStore."
version = "0.7.1"
readme = "README.md"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
readme = { workspace = true }
homepage = { workspace = true }
repository = { workspace = true }
documentation = "https://docs.rs/eventsourced-nats/latest/eventsourced-nats"
Expand Down
15 changes: 15 additions & 0 deletions eventsourced-nats/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# EventSourced NATS

[![Crates.io][crates-badge]][crates-url]
[![license][license-badge]][license-url]

[crates-badge]: https://img.shields.io/crates/v/eventsourced-nats
[crates-url]: https://crates.io/crates/eventsourced-nats
[license-badge]: https://img.shields.io/github/license/hseeberger/eventsourced
[license-url]: https://github.com/hseeberger/eventsourced/blob/main/LICENSE

NATS implementation for [`eventsourced`](https://github.com/hseeberger/eventsourced/blob/main/eventsourced/README.md) `EvtLog` and `SnapshotStore`.

## License ##

This code is open source software licensed under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0.html).
4 changes: 2 additions & 2 deletions eventsourced-postgres/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "eventsourced-postgres"
description = "Postgres implementation for eventsourced EvtLog and SnapshotStore."
description = "Postgres implementation for EventSourced EvtLog and SnapshotStore."
version = "0.7.2"
readme = "README.md"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
readme = { workspace = true }
homepage = { workspace = true }
repository = { workspace = true }
documentation = "https://docs.rs/eventsourced-postgres/latest/eventsourced-postgres"
Expand Down
15 changes: 15 additions & 0 deletions eventsourced-postgres/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# EventSourced Postgres

[![Crates.io][crates-badge]][crates-url]
[![license][license-badge]][license-url]

[crates-badge]: https://img.shields.io/crates/v/eventsourced-postgres
[crates-url]: https://crates.io/crates/eventsourced-postgres
[license-badge]: https://img.shields.io/github/license/hseeberger/eventsourced
[license-url]: https://github.com/hseeberger/eventsourced/blob/main/LICENSE

Postgres implementation for [`eventsourced`](https://github.com/hseeberger/eventsourced/blob/main/eventsourced/README.md) `EvtLog` and `SnapshotStore`.

## License ##

This code is open source software licensed under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0.html).
2 changes: 1 addition & 1 deletion eventsourced/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
name = "eventsourced"
description = "Event sourced entities in Rust."
version = "0.7.1"
readme = "README.md"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
readme = { workspace = true }
homepage = { workspace = true }
repository = { workspace = true }
documentation = "https://docs.rs/eventsourced/latest/eventsourced"
Expand Down
Loading