Skip to content

chore: bump up cqrs-es (0.5)#10

Closed
0xgleb wants to merge 1 commit into
chore/bump-up-depsfrom
chore/bump-up-cqrs-es
Closed

chore: bump up cqrs-es (0.5)#10
0xgleb wants to merge 1 commit into
chore/bump-up-depsfrom
chore/bump-up-cqrs-es

Conversation

@0xgleb

@0xgleb 0xgleb commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Motivation

Track the latest cqrs-es release and keep consumers on a single version. The
public EventSourced API is preserved; the migration is internal to the
Lifecycle adapter. Closes RAI-865.

Solution

Migrates the workspace from cqrs-es 0.4.12 to 0.5.0. cqrs-es 0.5 reworks the
aggregate command model and drops async-trait from its core traits.

  • Aggregate::handle is now &mut self, writes events through an EventSink,
    and returns (); fn aggregate_type() became const TYPE.
  • Aggregate, PersistedEventRepository, and ViewRepository are native
    async fn traits, so #[async_trait] is removed from those impls (it stays on
    Query and on our EventSourced/Reactor).
  • Lifecycle::handle adapts the new signature internally, so consumers' impls of
    EventSourced are untouched.
  • cqrs-es 0.5 rebuilds snapshots inside commit by re-applying handled events;
    Lifecycle rejects a re-applied genesis event, so handle leaves self at
    its pre-command state (driving the sink through a throwaway copy) and events
    apply exactly once.
  • Verified with cargo nextest run --workspace (96 pass), clippy, fmt, and both
    example crates on cqrs-es 0.5 + sqlx 0.9. Stacked on chore: bump up deps (sqlx 0.9) #9.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@0xgleb, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 59 minutes and 59 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 400e675a-f1e7-4600-8fea-e286d0da4489

📥 Commits

Reviewing files that changed from the base of the PR and between efcba38 and 7550476.

⛔ Files ignored due to path filters (3)
  • Cargo.lock is excluded by !**/*.lock
  • examples/complex/Cargo.lock is excluded by !**/*.lock
  • examples/simple/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • Cargo.toml
  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/lifecycle.rs
  • crates/event-sorcery/src/projection.rs
  • crates/event-sorcery/src/schema_registry.rs
  • crates/event-sorcery/src/sqlite_event_repository.rs
  • crates/event-sorcery/src/testing.rs
  • crates/sqlite-es/Cargo.toml
  • crates/sqlite-es/src/event_repository.rs
  • crates/sqlite-es/src/view_repository.rs
  • docs/cqrs.md
  • examples/complex/Cargo.toml
  • examples/simple/Cargo.toml

Walkthrough

This PR upgrades event-sorcery and sqlite-es from cqrs-es 0.4.12 to 0.5.0. The main library change replaces the event-returning handle() method with an EventSink output parameter and replaces the aggregate_type() method with a TYPE constant. All aggregate implementations, event repositories, tests, and examples are updated to match the new API.

Changes

cqrs-es 0.5.0 migration: EventSink and TYPE constant

Layer / File(s) Summary
Core Aggregate API and dependency upgrade
Cargo.toml, crates/event-sorcery/src/lifecycle.rs
Update cqrs-es to 0.5.0, implement new Aggregate contract in Lifecycle with const TYPE instead of aggregate_type() method, change handle signature to accept &mut self and &EventSink<Self> instead of returning Vec<Event>, remove #[async_trait] attribute, and use scratch clone to avoid double-applying events during snapshot rebuilding.
Lifecycle command handling and error tests
crates/event-sorcery/src/lifecycle.rs
Add EventSink import to test module and refactor command delegation tests to pass sink into handle and collect events via sink.collect().await; update error handling tests to supply sink parameter.
Event repository TYPE constant binding
crates/event-sorcery/src/sqlite_event_repository.rs
Update SqliteEventRepository to bind aggregate type using A::TYPE instead of calling A::aggregate_type() across event loading, snapshot operations, and stream replay; remove async_trait import and attribute; adjust reference semantics in SQL binding.
Projection aggregate type and async_trait removal
crates/event-sorcery/src/projection.rs
Update catch_up to derive aggregate type from Aggregate::TYPE and adjust parameter binding semantics; remove #[async_trait] attributes from test ViewRepository implementations while preserving async method bodies.
Test harness and schema registry event capture
crates/event-sorcery/src/testing.rs, crates/event-sorcery/src/schema_registry.rs
Update TestHarness::when to create EventSink, pass it into lifecycle.handle, and return collected events; refactor schema registry tests to route events through EventSink instead of handle return values.
sqlite-es repository TYPE binding and test updates
crates/sqlite-es/src/event_repository.rs
Update EventRepository to bind aggregate type via A::TYPE across load, snapshot, and stream paths; remove async_trait import and attribute; refactor test TestAggregate to use const TYPE and updated handle signature; update SerializedEvent fixtures to set aggregate_type from TestAggregate::TYPE.to_string().
sqlite-es view repository test aggregate
crates/sqlite-es/src/view_repository.rs
Update test module to import EventSink and replace TestAggregate Aggregate impl with one using const TYPE and updated handle signature accepting &mut self plus &EventSink<Self> returning Result<(), Error>.
Example crate dependency updates
examples/complex/Cargo.toml, examples/simple/Cargo.toml
Update cqrs-es dependency from 0.4.12 to 0.5.0 in both example crates.

Suggested reviewers

  • JuaniRios
  • findolor
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: upgrading the cqrs-es dependency from 0.4.12 to 0.5.0 across the workspace and adapting internal code.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/bump-up-cqrs-es

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

0xgleb commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more


How to use the Graphite Merge Queue

Add the label add-to-gt-merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has required the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@0xgleb 0xgleb force-pushed the chore/bump-up-cqrs-es branch 13 times, most recently from 9f72eb6 to 7217caf Compare June 5, 2026 01:31
@0xgleb 0xgleb force-pushed the chore/bump-up-deps branch from 6bb147e to 1bca65b Compare June 5, 2026 01:31
@0xgleb 0xgleb self-assigned this Jun 5, 2026
@0xgleb 0xgleb changed the title chore: bump up cqrs-es chore: bump up cqrs-es (0.5) Jun 5, 2026
@linear-code

linear-code Bot commented Jun 5, 2026

Copy link
Copy Markdown

RAI-865

@0xgleb 0xgleb requested review from JuaniRios and findolor June 8, 2026 22:43
@0xgleb 0xgleb force-pushed the chore/bump-up-cqrs-es branch from 7217caf to efcba38 Compare June 9, 2026 20:39
@graphite-app graphite-app Bot changed the base branch from chore/bump-up-deps to graphite-base/10 June 9, 2026 20:39
@0xgleb 0xgleb force-pushed the chore/bump-up-deps branch from 1bca65b to 5edb8fc Compare June 9, 2026 20:39
@0xgleb 0xgleb changed the base branch from graphite-base/10 to chore/bump-up-deps June 9, 2026 20:40
@0xgleb 0xgleb mentioned this pull request Jun 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/event-sorcery/src/lifecycle.rs (1)

523-552: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Assert sink stays empty when handle returns an error.

Add empty-event assertions in both error-path tests so no-events-on-error is explicitly enforced.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/event-sorcery/src/lifecycle.rs` around lines 523 - 552, In both tests
handle_maps_domain_error_to_lifecycle_apply and
handle_failed_returns_stored_error, after calling lifecycle.handle(...) and
unwrapping the error, add an assertion that the EventSink instance sink contains
no emitted events (i.e., assert the sink is empty) to ensure no events are
produced on error; locate the sink variable (EventSink::default()) and use its
appropriate emptiness check (for example sink.is_empty() or inspecting
sink.events.len() == 0) to enforce the no-events-on-error invariant.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/event-sorcery/src/lifecycle.rs`:
- Around line 493-520: Both tests currently assert emitted events but don't
verify that the Lifecycle value itself is unchanged by handle; clone the initial
lifecycle state before calling Lifecycle::handle (e.g., let original =
lifecycle.clone()) and after the await unwrap assert equality
(assert_eq!(lifecycle, original)) to lock in the scratch-copy contract for both
handle_uninitialized_delegates_to_initialize (Lifecycle::<Counter>::default())
and handle_live_delegates_to_transition (Lifecycle::Live(Counter { value: 0 })).
Ensure Lifecycle implements Clone/PartialEq where needed and use the same
variable names (lifecycle, original) so it's clear which state is being
compared.
- Around line 151-153: The match arm that returns Err(error.clone()) and the
subsequent .map_err(LifecycleError::Apply) silently propagate command/aggregate
errors; update the aggregate's handle(...) implementation to log those error
paths before returning or converting: add a structured log (warn/error) inside
the Self::Failed { error, .. } => branch immediately before return
Err(error.clone()), and also log the error inside the Result.map_err(...)
closure (i.e., before converting to LifecycleError::Apply) so both raw command
failures and mapped domain errors are recorded; reference the handle method, the
Self::Failed pattern, and LifecycleError::Apply when making these changes.

In `@crates/sqlite-es/src/event_repository.rs`:
- Around line 350-356: Reorder the imports into the required three-group layout
(external -> workspace -> crate-internal): move std::fmt and
serde::{Deserialize, Serialize} into the external group first, then place
cqrs_es::DomainEvent and cqrs_es::event_sink::EventSink into the workspace
group, and finally keep crate::testing::create_test_pool and use super::* in the
crate-internal group; also alphabetize imports within each group for consistency
and preserve existing symbols (DomainEvent, EventSink, create_test_pool,
super::*).

---

Outside diff comments:
In `@crates/event-sorcery/src/lifecycle.rs`:
- Around line 523-552: In both tests handle_maps_domain_error_to_lifecycle_apply
and handle_failed_returns_stored_error, after calling lifecycle.handle(...) and
unwrapping the error, add an assertion that the EventSink instance sink contains
no emitted events (i.e., assert the sink is empty) to ensure no events are
produced on error; locate the sink variable (EventSink::default()) and use its
appropriate emptiness check (for example sink.is_empty() or inspecting
sink.events.len() == 0) to enforce the no-events-on-error invariant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 69eababc-6abf-43ce-9d4d-57d229f8eb5a

📥 Commits

Reviewing files that changed from the base of the PR and between 5edb8fc and efcba38.

⛔ Files ignored due to path filters (3)
  • Cargo.lock is excluded by !**/*.lock
  • examples/complex/Cargo.lock is excluded by !**/*.lock
  • examples/simple/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml
  • crates/event-sorcery/src/lifecycle.rs
  • crates/event-sorcery/src/projection.rs
  • crates/event-sorcery/src/schema_registry.rs
  • crates/event-sorcery/src/sqlite_event_repository.rs
  • crates/event-sorcery/src/testing.rs
  • crates/sqlite-es/src/event_repository.rs
  • crates/sqlite-es/src/view_repository.rs
  • examples/complex/Cargo.toml
  • examples/simple/Cargo.toml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: examples
  • GitHub Check: test
🧰 Additional context used
📓 Path-based instructions (5)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

When handling clippy errors about function lengths or cognitive complexity, don't split up functions more than necessary. Instead ask the user if we can add a clippy allow.

Files:

  • crates/event-sorcery/src/testing.rs
  • crates/event-sorcery/src/schema_registry.rs
  • crates/event-sorcery/src/projection.rs
  • crates/event-sorcery/src/lifecycle.rs
  • crates/sqlite-es/src/view_repository.rs
  • crates/event-sorcery/src/sqlite_event_repository.rs
  • crates/sqlite-es/src/event_repository.rs
crates/event-sorcery/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

The Lifecycle enum is pub(crate) deliberately—it's an implementation detail of the EventSourced ↔ cqrs-es bridge and must not leak through any public bound.

Files:

  • crates/event-sorcery/src/testing.rs
  • crates/event-sorcery/src/schema_registry.rs
  • crates/event-sorcery/src/projection.rs
  • crates/event-sorcery/src/lifecycle.rs
  • crates/event-sorcery/src/sqlite_event_repository.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Package by Feature, Not by Layer. Organize by business domain, not technical layers. FORBIDDEN catch-all modules: types.rs, error.rs, models.rs, utils.rs, helpers.rs, http.rs, dto.rs, entities.rs, services.rs, domain.rs. CORRECT: lifecycle.rs, projection.rs, reactor.rs, view_backend.rs. Each feature module contains ALL related code.
CRITICAL: NEVER write directly to the events table — no direct INSERTs, no manual sequence numbers, no bypassing CqrsFramework. Always use CqrsFramework::execute() or execute_with_metadata() to emit events through aggregate commands.
CRITICAL: Single CQRS Framework Instance Per Aggregate. In any consuming application, each aggregate must have exactly ONE SqliteCqrs, constructed once at startup. Never call sqlite_cqrs() or CqrsFramework::new() per request. Direct construction is fine in test/CLI/migration code.
Use cqrs-es Services for side-effects in handle() to ensure atomicity with events. Naming: {Action}er trait -> {Domain}Service implements -> {Domain}Manager orchestrates.
Log in command handlers, not callers. All logging for command execution belongs in the aggregate's handle() method. The handler has full aggregate state making log messages rich without the caller needing extra context.
Make invalid states unrepresentable through the type system. Use ADTs and enums to encode business rules and state transitions directly in types rather than runtime validation.
Accept domain newtypes and convert to SDK primitives inside the callee. Exception: cross-crate boundaries where the callee can't depend on caller's domain types — destructure at the call site.
Favor FP/ADT patterns over OOP. Use pattern matching, combinators, type-driven design. Prefer iterators over imperative loops unless it increases complexity. Use itertools for richer iterator chains.
Follow comprehensive commenting guidelines. Comment complex business logic, algorithm rationale, external system behavior, non-obvious constraints, test data context, ...

Files:

  • crates/event-sorcery/src/testing.rs
  • crates/event-sorcery/src/schema_registry.rs
  • crates/event-sorcery/src/projection.rs
  • crates/event-sorcery/src/lifecycle.rs
  • crates/sqlite-es/src/view_repository.rs
  • crates/event-sorcery/src/sqlite_event_repository.rs
  • crates/sqlite-es/src/event_repository.rs
Cargo.toml

📄 CodeRabbit inference engine (AGENTS.md)

Workspace deps live in [workspace.dependencies]; per-crate Cargo.toml files use .workspace = true (with optional per-crate features added on top).

Files:

  • Cargo.toml
*

⚙️ CodeRabbit configuration file

Focus on providing constructive criticism. Whenever you see a suboptimal approach, suggest more idiomatic or robust alternative(s). Flag potential footguns. Suggest FP alternatives to mutable/imperative code. Point out architectural flaws like leaky abstractions, tight coupling, wrong level of abstraction, poor type modeling, over-abstraction, unclear domain boundaries. Code should generally be organized based on business concerns rather than technical aspects - suggest improvements if you find violations. Point out gaps in test coverage but suggest tests that are not too coupled to the implementation and actually test domain invariants and business logic

Files:

  • Cargo.toml
🔇 Additional comments (15)
examples/complex/Cargo.toml (1)

12-12: LGTM!

examples/simple/Cargo.toml (1)

13-13: LGTM!

crates/event-sorcery/src/sqlite_event_repository.rs (5)

60-60: LGTM!


81-81: LGTM!


99-99: LGTM!


152-152: LGTM!


176-176: LGTM!

Also applies to: 189-189, 202-202

crates/event-sorcery/src/projection.rs (3)

226-226: LGTM!

Also applies to: 243-243, 258-258


706-737: LGTM!


793-839: LGTM!

Cargo.toml (1)

38-38: LGTM!

crates/event-sorcery/src/testing.rs (1)

12-12: LGTM!

Also applies to: 87-91

crates/event-sorcery/src/schema_registry.rs (1)

266-267: LGTM!

Also applies to: 272-288, 308-323, 336-351

crates/sqlite-es/src/event_repository.rs (1)

150-182: LGTM!

Also applies to: 237-237, 256-256, 263-263, 270-270, 393-405, 419-419, 446-446, 494-494, 503-503, 512-512

crates/sqlite-es/src/view_repository.rs (1)

200-200: LGTM!

Also applies to: 242-255

Comment thread crates/event-sorcery/src/lifecycle.rs Outdated
Comment thread crates/event-sorcery/src/lifecycle.rs
Comment thread crates/sqlite-es/src/event_repository.rs Outdated
@0xgleb 0xgleb force-pushed the chore/bump-up-deps branch from 1bca65b to bfb4203 Compare June 9, 2026 21:22
@0xgleb 0xgleb force-pushed the chore/bump-up-cqrs-es branch from efcba38 to 99bbb51 Compare June 9, 2026 21:22
@0xgleb 0xgleb requested review from JuaniRios and findolor June 9, 2026 22:21
@0xgleb 0xgleb force-pushed the chore/bump-up-deps branch from bfb4203 to c151c42 Compare June 9, 2026 22:56
@0xgleb 0xgleb force-pushed the chore/bump-up-cqrs-es branch from 99bbb51 to 6228296 Compare June 9, 2026 22:56

@findolor findolor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved.

@graphite-app

graphite-app Bot commented Jun 10, 2026

Copy link
Copy Markdown

Merge activity

  • Jun 10, 5:25 PM UTC: 0xgleb added this pull request to the Graphite merge queue.
  • Jun 10, 5:26 PM UTC: CI is running for this pull request on a draft pull request (#21) due to your merge queue CI optimization settings.
  • Jun 10, 5:30 PM UTC: Merged by the Graphite merge queue via draft PR: #21.

@graphite-app graphite-app Bot closed this Jun 10, 2026
@graphite-app graphite-app Bot deleted the chore/bump-up-cqrs-es branch June 10, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants