Skip to content

Commit 2c91b45

Browse files
authored
refactor: changes to aggregate::Repository, event::Store, documentation and more (#289)
In the scope of a closer v0.5.0 release, this PR introduces quite some refactorings to different APIs, which are still experimental: ### refactor: generalize `event::StreamVersionExpected` into `version::Check` `version::Check` is a more general phrasing and naming for optimistic locking, yet expressing quite more precisely its role. ### feat: add new `aggregate::test::Scenario` It is now possible to write test assertions on `aggregate::Root` methods, rather than having that only available through a `command::test::Scenario`. ### refactor(aggregate::repository): split `Repository` traits once again, use concrete error type After trying the approach of having an unified `aggregate::Repository` trait, and use an `AnyRepository` with `Error = anyhow::Error` (so, opaque) for better usage in trait objects, we've reverted back to Interface Segregation: 1. Have two separate interfaces -- `Saver` and `Getter` -- and a `Repository` one, which extends both, 2. The errors returned by those are concrete types, using `thiserror`, rather than using an associated type to abstract over it. While this has required the introduction of opaque errors with `anyhow::Error` in the concrete error types, this should provide a better ergonomics for consumers of this API. ### refactor(event::store): move symbols to this module, add `AppendError` concrete type The `event::Store` follows the same approach as `aggregate::Repository` now: split interfaces, concrete error types. The only exception being `Streamer::stream`, where it seems to be fine to abstract over the error type returned (for now). ### refactor(eventually-macros): remove derive(Message), as it requires better design `#[derive(Message)]` requires better design; we've implemented that with a specific idea in mind, but that's very limiting and wouldn't be used most of the time. We remove it for now while we collect some ideas on how it should work. ### chore: add vscode setting for rust-analyzer to use all features Finally rust-analyzer under VSCode will compile and check the workspace using all feature flags. ### docs(eventually): add missing documentation There was A LOT of missing documentation. This PR addresses some of the missing stuff. It's not great by any means, we'd likely need some dedicated effort before releasing. ### refactor: split Serde trait into separate ones, implement `serde::Convert` and `serde::ProtoJson` After some production usage, we've collected some different ideas on how this trait should work, and have implemented that following the previous concepts: split methods/traits, concrete error types. ### chore: update all versions Too many open dependabot PRs, this will take care of them all at once (but `syn`, which is a major change).
1 parent be239c7 commit 2c91b45

35 files changed

Lines changed: 1222 additions & 1010 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,11 @@ jobs:
148148
- name: Checkout sources
149149
uses: actions/checkout@v4
150150

151-
- name: Install nightly toolchain
151+
- name: Install stable toolchain
152152
uses: actions-rs/toolchain@v1
153153
with:
154154
profile: minimal
155-
toolchain: nightly
155+
toolchain: stable
156156
override: true
157157
components: clippy,rustfmt
158158

.vscode/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"rust-analyzer.cargo.features": "all",
3+
"rust-analyzer.check.command": "clippy"
4+
}

eventually-macros/Cargo.toml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
[package]
22
name = "eventually-macros"
3+
description = "Macros for eventually crate"
34
version = "0.1.0"
45
edition = "2021"
6+
authors = ["Danilo Cianfrone <danilocianfr@gmail.com>"]
7+
license = "MIT"
8+
readme = "../README.md"
9+
repository = "https://github.com/get-eventually/eventually-rs"
10+
11+
categories = [
12+
"rust-patterns",
13+
"web-programming",
14+
"asynchronous",
15+
"data-structures",
16+
]
17+
keywords = ["architecture", "ddd", "event-sourcing", "cqrs", "es"]
518

619
[lib]
720
proc-macro = true
821

922
[dependencies]
1023
syn = { version = "1.0.109", features = ["full"] }
11-
quote = "1.0.33"
24+
quote = "1.0.35"
1225
eventually = { path = "../eventually" }

eventually-macros/src/lib.rs

Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,21 @@
1-
//! Module containing useful macros for the [eventually] crate.
1+
//! `eventually-macros` contains useful macros that provides
2+
//! different implementations of traits and functionalities from [eventually].
23
3-
#![deny(unsafe_code, unused_qualifications, trivial_casts)]
4-
#![warn(missing_docs)]
5-
#![deny(clippy::all)]
6-
#![warn(clippy::pedantic)]
4+
#![deny(unsafe_code, unused_qualifications, trivial_casts, missing_docs)]
5+
#![deny(clippy::all, clippy::pedantic, clippy::cargo)]
76

87
use proc_macro::TokenStream;
98
use quote::quote;
10-
use syn::{parse_macro_input, AttributeArgs, Fields, ItemEnum, ItemStruct, Meta, NestedMeta, Path};
9+
use syn::{parse_macro_input, AttributeArgs, Fields, ItemStruct, Meta, NestedMeta, Path};
1110

12-
#[proc_macro_derive(Message)]
13-
pub fn derive_message(input: TokenStream) -> TokenStream {
14-
let item = parse_macro_input!(input as ItemEnum);
15-
let item_name = item.ident;
16-
let event_prefix = item_name
17-
.to_string()
18-
.strip_suffix("Event")
19-
.unwrap()
20-
.to_owned();
21-
22-
let match_cases = item.variants.iter().fold(quote! {}, |acc, variant| {
23-
let event_type = &variant.ident;
24-
let event_name = format!("{}{}", event_prefix, event_type);
25-
26-
quote! {
27-
#acc
28-
#item_name::#event_type { .. } => #event_name,
29-
}
30-
});
31-
32-
let result = quote! {
33-
impl eventually::message::Message for #item_name {
34-
fn name(&self) -> &'static str {
35-
match self {
36-
#match_cases
37-
}
38-
}
39-
}
40-
};
41-
42-
result.into()
43-
}
44-
45-
/// Implements a newtype to use the [eventually::aggregate::Root] instance with
46-
/// user-defined [eventually::aggregate::Aggregate] types.
11+
/// Implements a newtype to use the [`eventually::aggregate::Root`] instance with
12+
/// user-defined [`eventually::aggregate::Aggregate`] types.
4713
///
4814
/// # Context
4915
///
50-
/// The eventually API uses `aggregate::Root<T>` to manage the versioning and
51-
/// list of events to commit for an `Aggregate` instance. Domain commands
52-
/// are to be implemented on the `aggregate::Root<T>` instance, as it gives
16+
/// The eventually API uses [`aggregate::Root`][eventually::aggregate::Root]
17+
/// to manage the versioning and list of events to commit for an `Aggregate` instance.
18+
/// Domain commands are to be implemented on the `aggregate::Root<T>` instance, as it gives
5319
/// access to use `Root<T>.record_that` or `Root<T>.record_new` to record Domain Events.
5420
///
5521
/// However, it's not possible to use `impl aggregate::Root<MyAggregateType>` (`MyAggregateType`
@@ -58,7 +24,11 @@ pub fn derive_message(input: TokenStream) -> TokenStream {
5824
///
5925
/// This attribute macro makes the implementation of a newtype easy, as it Implements
6026
/// conversion traits from and to `aggregate::Root<T>` and implements automatic deref
61-
/// through [std::ops::Deref] and [std::ops::DerefMut].
27+
/// through [`std::ops::Deref`] and [`std::ops::DerefMut`].
28+
///
29+
/// # Panics
30+
///
31+
/// This method will panic if the Aggregate Root type is not provided as a macro parameter.
6232
#[proc_macro_attribute]
6333
pub fn aggregate_root(args: TokenStream, item: TokenStream) -> TokenStream {
6434
let args = parse_macro_input!(args as AttributeArgs);

eventually-postgres/Cargo.toml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,27 @@ categories = ["web-programming", "asynchronous"]
1212
keywords = ["postgres", "postgresql", "database", "ddd", "event-sourcing"]
1313

1414
[dependencies]
15-
async-trait = "0.1.74"
16-
chrono = "0.4.31"
15+
anyhow = "1.0.80"
16+
async-trait = "0.1.77"
17+
chrono = "0.4.34"
1718
eventually = { path = "../eventually", version = "0.5.0", features = [
1819
"serde-json",
1920
] }
20-
futures = "0.3.29"
21+
futures = "0.3.30"
2122
lazy_static = "1.4.0"
22-
regex = "1.10.2"
23-
sqlx = { version = "0.7.2", features = [
23+
regex = "1.10.3"
24+
sqlx = { version = "0.7.3", features = [
2425
"runtime-tokio-rustls",
2526
"postgres",
2627
"migrate",
2728
] }
28-
thiserror = "1.0.50"
29+
thiserror = "1.0.57"
2930

3031
[dev-dependencies]
31-
tokio = { version = "1.34.0", features = ["macros", "rt"] }
32+
tokio = { version = "1.36.0", features = ["macros", "rt"] }
3233
eventually = { path = "../eventually", version = "0.5.0", features = [
3334
"serde-json",
3435
] }
3536
eventually-macros = { path = "../eventually-macros", version = "0.1.0" }
36-
serde = { version = "1.0.192", features = ["derive"] }
37+
serde = { version = "1.0.197", features = ["derive"] }
3738
rand = "0.8.5"

0 commit comments

Comments
 (0)