Skip to content

feat: replace SqliteProjectionRepo with RepoFamily GAT#6

Merged
0xgleb merged 2 commits into
feat/cifrom
feat/deferred-types
May 14, 2026
Merged

feat: replace SqliteProjectionRepo with RepoFamily GAT#6
0xgleb merged 2 commits into
feat/cifrom
feat/deferred-types

Conversation

@0xgleb

@0xgleb 0xgleb commented May 9, 2026

Copy link
Copy Markdown
Collaborator

Closes RAI-465.

What

Introduces a ViewBackend trait and SqliteViewBackend struct, replacing the previous SqliteProjectionRepo newtype. Projection is now generic over Backend: ViewBackend instead of a raw Repo type parameter. SqliteProjectionRepo is removed from the public API, and SqliteViewBackend is exported in its place alongside the new ViewBackend trait.

Why

The previous design used SqliteProjectionRepo as a newtype solely to hide the pub(crate) Lifecycle type from public bounds on Projection. This was a leaky workaround that required #[allow(private_bounds)] suppressions and left a TODO about proper higher-kinded type (HKT) support. The new ViewBackend trait uses a GAT (type Repo<View, Agg>) to emulate HKT, allowing Projection to resolve its repository type internally without ever surfacing Lifecycle in public where clauses. This removes the need for the #[allow(private_bounds)] suppressions and makes the backend genuinely pluggable.

How

A new view_backend module defines ViewBackend as a trait with a single GAT Repo<View, Agg>, and SqliteViewBackend as the default implementation mapping every (View, Agg) pair to SqliteViewRepository. Projection replaces its Repo type parameter with Backend: ViewBackend and stores Arc<Backend::Repo<Lifecycle<Entity>, Lifecycle<Entity>>> directly. The 'static bound is added to EventSourced to satisfy the GAT constraints. Test helpers (InMemoryRepo, ConflictingRepo) are made generic over (View, Agg) and wrapped in corresponding InMemoryViewBackend and ConflictingViewBackend impls of ViewBackend.

Summary by CodeRabbit

  • Breaking Changes

    • The Projection API now uses a pluggable backend abstraction instead of a fixed repository type, requiring updates to generic parameters in client code.
    • A previously exported repository type is no longer publicly available; applications must use the new backend abstraction.
  • New Features

    • Introduced a new abstraction layer enabling custom view storage backends for greater flexibility.

Review Change Stack

@0xgleb 0xgleb mentioned this pull request May 9, 2026
@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@0xgleb has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 22 minutes and 16 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c2df79b0-dd6b-49a8-bce9-e46e3593eb26

📥 Commits

Reviewing files that changed from the base of the PR and between f31b57c and 0845e34.

⛔ Files ignored due to path filters (2)
  • examples/complex/Cargo.lock is excluded by !**/*.lock
  • examples/simple/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • .github/workflows/ci.yaml
  • Cargo.toml
  • README.md
  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/projection.rs
  • crates/event-sorcery/src/view_backend.rs
  • examples/README.md
  • examples/complex/Cargo.toml
  • examples/complex/README.md
  • examples/complex/migrations/20260513104032_init.sql
  • examples/complex/migrations/20260513104033_inventory_view.sql
  • examples/complex/src/audit_log.rs
  • examples/complex/src/inventory.rs
  • examples/complex/src/main.rs
  • examples/complex/src/order.rs
  • examples/complex/src/stock_alert.rs
  • examples/simple/Cargo.toml
  • examples/simple/README.md
  • examples/simple/migrations/20260513104023_init.sql
  • examples/simple/migrations/20260513104024_support_ticket_view.sql
  • examples/simple/src/main.rs
  • examples/simple/src/support_ticket.rs
  • flake.nix
  • scripts/check-examples.nu

Walkthrough

This PR refactors the materialized-view storage layer in event-sorcery from a concrete SqliteProjectionRepo wrapper to a pluggable ViewBackend trait with Generic Associated Types (GATs). Projection is now generic over Backend: ViewBackend instead of a concrete repository type. Test infrastructure is generalized to support multiple backends, and public API exports are updated accordingly.

Changes

ViewBackend Abstraction and Projection Refactor

Layer / File(s) Summary
View Backend Abstraction
crates/event-sorcery/src/view_backend.rs
Introduces ViewBackend trait with a GAT type Repo<View, Agg> for mapping view/aggregate pairs to concrete repositories. Provides SqliteViewBackend as the default implementation.
Projection Struct and Core Methods
crates/event-sorcery/src/projection.rs (doc, struct, constructors)
Changes Projection from Projection<Entity, Repo = SqliteProjectionRepo<Entity>> to Projection<Entity, Backend: ViewBackend = SqliteViewBackend>. Updates sqlite() and new() to work with backend-associated repo types.
Projection Trait Implementations
crates/event-sorcery/src/projection.rs (Clone, Dependent, Reactor)
Clone, Dependent, and Reactor trait bounds are expressed via Backend: ViewBackend instead of direct repository requirements.
Generalized Test Repositories
crates/event-sorcery/src/projection.rs (test modules)
InMemoryRepo<View, Agg> and ConflictingRepo<View, Agg> are generalized from Lifecycle<TestEntity>-specific implementations. Test backend adapters InMemoryViewBackend and ConflictingViewBackend implement the ViewBackend trait.
Test Case Wiring
crates/event-sorcery/src/projection.rs (test functions)
Existing behavioral tests are updated to construct Projection instances via backend-typed repos and the Projection::new() constructor.
Public API Exports
crates/event-sorcery/src/lib.rs
Declares the new view_backend module and updates re-exports: removes SqliteProjectionRepo, adds ViewBackend and SqliteViewBackend. Reformats EventSourced trait declaration (whitespace only).
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: replacing SqliteProjectionRepo with a RepoFamily GAT-based abstraction (ViewBackend trait), which aligns with the PR's core objective of refactoring the projection repository abstraction.
Docstring Coverage ✅ Passed Docstring coverage is 86.36% 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.

✏️ 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 feat/deferred-types

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 May 9, 2026

Copy link
Copy Markdown
Collaborator Author

@0xgleb 0xgleb changed the base branch from feat/ci to graphite-base/6 May 9, 2026 09:47
@0xgleb 0xgleb changed the base branch from graphite-base/6 to feat/ci May 9, 2026 09:50
@0xgleb 0xgleb changed the base branch from feat/ci to graphite-base/6 May 9, 2026 09:50
@0xgleb 0xgleb self-assigned this May 9, 2026
@linear

linear Bot commented May 9, 2026

Copy link
Copy Markdown

RAI-465

@0xgleb 0xgleb force-pushed the graphite-base/6 branch from ee4254c to ad25a5e Compare May 9, 2026 10:00
@0xgleb 0xgleb force-pushed the feat/deferred-types branch from 6a5725e to f5ef365 Compare May 9, 2026 10:00
@0xgleb 0xgleb changed the base branch from graphite-base/6 to feat/ci May 9, 2026 10:01
@0xgleb 0xgleb force-pushed the feat/deferred-types branch from f5ef365 to 2ee60bc Compare May 9, 2026 10:08
@0xgleb 0xgleb force-pushed the feat/deferred-types branch from 2ee60bc to 0985459 Compare May 9, 2026 10:13
@0xgleb 0xgleb changed the base branch from feat/ci to graphite-base/6 May 9, 2026 10:34
@0xgleb 0xgleb force-pushed the graphite-base/6 branch from 5df246f to 51ef4ef Compare May 9, 2026 10:41
@0xgleb 0xgleb force-pushed the feat/deferred-types branch from 0985459 to d41a5cb Compare May 9, 2026 10:41
@0xgleb 0xgleb changed the base branch from graphite-base/6 to feat/ci May 9, 2026 10:42
@0xgleb 0xgleb force-pushed the feat/deferred-types branch from d41a5cb to 71f7e98 Compare May 9, 2026 11:07
@0xgleb 0xgleb changed the base branch from feat/ci to graphite-base/6 May 9, 2026 13:08
@0xgleb 0xgleb marked this pull request as ready for review May 9, 2026 13:21
@0xgleb 0xgleb force-pushed the feat/deferred-types branch from 71f7e98 to f31b57c Compare May 9, 2026 13:49
@0xgleb 0xgleb force-pushed the graphite-base/6 branch from 618047a to 1670c19 Compare May 9, 2026 13:49
@0xgleb 0xgleb changed the base branch from graphite-base/6 to feat/ci May 9, 2026 13:49
@0xgleb 0xgleb mentioned this pull request May 9, 2026

0xgleb commented May 9, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

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/projection.rs (1)

131-146: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Expose a public constructor for custom ViewBackends.

ViewBackend is now public, but the only public constructor still builds SqliteViewBackend, and the backend-agnostic new stays #[cfg(test)] pub(crate). That means downstream code can write Projection<MyEntity, MyBackend> but has no public way to instantiate it, so the new abstraction is effectively unusable outside this crate. Please either add a public backend-agnostic factory or keep ViewBackend internal until that constructor exists.

Also applies to: 438-447

🤖 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/projection.rs` around lines 131 - 146, The public
ViewBackend type lacks a public, backend-agnostic constructor for Projection:
currently only backend-specific constructors like sqlite (pub fn sqlite) exist
and the generic Projection::new is #[cfg(test)] pub(crate), so external crates
cannot instantiate Projection with custom ViewBackend implementations; add a
public constructor (e.g., pub fn new(repo: Arc<dyn
ViewBackend<Lifecycle<Entity>, Lifecycle<Entity>> + Send + Sync>, pool:
Option<SqlitePool>, table_name: Option<String>) -> Self) or make the existing
generic factory (Projection::new) pub (remove #[cfg(test)] and change visibility
to pub) so callers can create Projection<T, B> from any backend implementation,
and mirror the same change for the other backend-specific constructors
referenced around the lines noted (e.g., the similar constructor at 438-447) so
all backends are constructible externally.
🤖 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.

Outside diff comments:
In `@crates/event-sorcery/src/projection.rs`:
- Around line 131-146: The public ViewBackend type lacks a public,
backend-agnostic constructor for Projection: currently only backend-specific
constructors like sqlite (pub fn sqlite) exist and the generic Projection::new
is #[cfg(test)] pub(crate), so external crates cannot instantiate Projection
with custom ViewBackend implementations; add a public constructor (e.g., pub fn
new(repo: Arc<dyn ViewBackend<Lifecycle<Entity>, Lifecycle<Entity>> + Send +
Sync>, pool: Option<SqlitePool>, table_name: Option<String>) -> Self) or make
the existing generic factory (Projection::new) pub (remove #[cfg(test)] and
change visibility to pub) so callers can create Projection<T, B> from any
backend implementation, and mirror the same change for the other
backend-specific constructors referenced around the lines noted (e.g., the
similar constructor at 438-447) so all backends are constructible externally.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 488fb6d4-e637-4b1a-a539-b092da08a222

📥 Commits

Reviewing files that changed from the base of the PR and between 1670c19 and f31b57c.

📒 Files selected for processing (3)
  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/projection.rs
  • crates/event-sorcery/src/view_backend.rs
📜 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). (2)
  • GitHub Check: Graphite / mergeability_check
  • GitHub Check: Graphite / mergeability_check

The previous design parameterized Projection over a Repo type that was bound to ViewRepository<Lifecycle<Entity>, Lifecycle<Entity>>. That bound named the pub(crate) Lifecycle type in a public position, tripping the private_bounds lint, and was suppressed with #[allow(private_bounds)] in three impl blocks pending the crate's extraction.

ViewBackend is a GAT-on-trait HKT emulation: a ViewBackend instance is a type-level function (View, Aggregate) -> SomeRepo. Projection<Entity, Backend: ViewBackend> applies the function internally to obtain Backend::Repo<Lifecycle<Entity>, Lifecycle<Entity>>, so Lifecycle saturation happens inside the struct/method bodies, not in any public bound. SqliteViewBackend is the default. The three #[allow(private_bounds)] suppressions are removed. Test repos InMemoryRepo<View, Agg> and ConflictingRepo<View, Agg> made generic with paired ViewBackend adapters so existing tests still compile.
Adds a runnable examples/ directory at the workspace root covering the
core event-sorcery surface:

- basic_entity: smallest setup -- EventSourced, Store, send/load,
  load_entity, send_command, load_all_ids, count_aggregates,
  load_ids_paginated, compact_events, incremental_vacuum.
- projection: Materialized = Table with a SQLite generated column for
  filtered queries; load/load_all/filter/rebuild/rebuild_all; injects a
  domain Service via Arc<dyn Clock>.
- reactor: multi-entity StockAlert reactor wired across two stores plus
  a single-entity AuditLog reactor running alongside an auto-projection.

Each example has a README.md explaining the concept and a #[cfg(all(test,
feature = "test-support"))] mod tests block exercising replay,
TestHarness, TestStore, SpyReactor, and ReactorHarness. examples/README.md
indexes the three; the repo-root README.md links to it.

CI now runs every example in a dedicated matrix job and includes
--all-targets so example test modules execute under cargo nextest.
@0xgleb 0xgleb merged commit 16e8957 into feat/ci May 14, 2026
13 of 14 checks passed
@0xgleb 0xgleb deleted the feat/deferred-types branch May 14, 2026 15:39
0xgleb added a commit that referenced this pull request May 14, 2026
* feat: GitHub Actions CI

Matrix runs cargo check / nextest / clippy / fmt --check, plus a separate job for pre-commit hooks. Every step uses 'nix develop -c …' so CI runs the same toolchain as local dev. Caches ~/.cargo and target/ keyed on lockfile + flake.lock.

* feat: replace SqliteProjectionRepo with RepoFamily GAT (#6)

* feat: replace SqliteProjectionRepo with ViewBackend GAT

The previous design parameterized Projection over a Repo type that was bound to ViewRepository<Lifecycle<Entity>, Lifecycle<Entity>>. That bound named the pub(crate) Lifecycle type in a public position, tripping the private_bounds lint, and was suppressed with #[allow(private_bounds)] in three impl blocks pending the crate's extraction.

ViewBackend is a GAT-on-trait HKT emulation: a ViewBackend instance is a type-level function (View, Aggregate) -> SomeRepo. Projection<Entity, Backend: ViewBackend> applies the function internally to obtain Backend::Repo<Lifecycle<Entity>, Lifecycle<Entity>>, so Lifecycle saturation happens inside the struct/method bodies, not in any public bound. SqliteViewBackend is the default. The three #[allow(private_bounds)] suppressions are removed. Test repos InMemoryRepo<View, Agg> and ConflictingRepo<View, Agg> made generic with paired ViewBackend adapters so existing tests still compile.

* docs: add examples of how to use the repo (#7)

Adds a runnable examples/ directory at the workspace root covering the
core event-sorcery surface:

- basic_entity: smallest setup -- EventSourced, Store, send/load,
  load_entity, send_command, load_all_ids, count_aggregates,
  load_ids_paginated, compact_events, incremental_vacuum.
- projection: Materialized = Table with a SQLite generated column for
  filtered queries; load/load_all/filter/rebuild/rebuild_all; injects a
  domain Service via Arc<dyn Clock>.
- reactor: multi-entity StockAlert reactor wired across two stores plus
  a single-entity AuditLog reactor running alongside an auto-projection.

Each example has a README.md explaining the concept and a #[cfg(all(test,
feature = "test-support"))] mod tests block exercising replay,
TestHarness, TestStore, SpyReactor, and ReactorHarness. examples/README.md
indexes the three; the repo-root README.md links to it.

CI now runs every example in a dedicated matrix job and includes
--all-targets so example test modules execute under cargo nextest.
0xgleb added a commit that referenced this pull request May 14, 2026
* feat: AGENTS.md, SPEC.md, README, and docs

AGENTS.md adapted from upstream st0x conventions, trimmed to the rules that apply here (drops alloy/brokerage/onchain bits). SPEC.md describes the library's goals, components, and behavior. docs/domain.md is the CQRS/ES glossary and naming conventions. docs/cqrs.md is a usage reference. docs/sqlx.md is the running pitfall log. docs/ttdd.md is the type-driven TDD methodology. CLAUDE.md is a symlink to AGENTS.md so both names resolve to the same source of truth.

* feat: GitHub Actions CI (#5)

* feat: GitHub Actions CI

Matrix runs cargo check / nextest / clippy / fmt --check, plus a separate job for pre-commit hooks. Every step uses 'nix develop -c …' so CI runs the same toolchain as local dev. Caches ~/.cargo and target/ keyed on lockfile + flake.lock.

* feat: replace SqliteProjectionRepo with RepoFamily GAT (#6)

* feat: replace SqliteProjectionRepo with ViewBackend GAT

The previous design parameterized Projection over a Repo type that was bound to ViewRepository<Lifecycle<Entity>, Lifecycle<Entity>>. That bound named the pub(crate) Lifecycle type in a public position, tripping the private_bounds lint, and was suppressed with #[allow(private_bounds)] in three impl blocks pending the crate's extraction.

ViewBackend is a GAT-on-trait HKT emulation: a ViewBackend instance is a type-level function (View, Aggregate) -> SomeRepo. Projection<Entity, Backend: ViewBackend> applies the function internally to obtain Backend::Repo<Lifecycle<Entity>, Lifecycle<Entity>>, so Lifecycle saturation happens inside the struct/method bodies, not in any public bound. SqliteViewBackend is the default. The three #[allow(private_bounds)] suppressions are removed. Test repos InMemoryRepo<View, Agg> and ConflictingRepo<View, Agg> made generic with paired ViewBackend adapters so existing tests still compile.

* docs: add examples of how to use the repo (#7)

Adds a runnable examples/ directory at the workspace root covering the
core event-sorcery surface:

- basic_entity: smallest setup -- EventSourced, Store, send/load,
  load_entity, send_command, load_all_ids, count_aggregates,
  load_ids_paginated, compact_events, incremental_vacuum.
- projection: Materialized = Table with a SQLite generated column for
  filtered queries; load/load_all/filter/rebuild/rebuild_all; injects a
  domain Service via Arc<dyn Clock>.
- reactor: multi-entity StockAlert reactor wired across two stores plus
  a single-entity AuditLog reactor running alongside an auto-projection.

Each example has a README.md explaining the concept and a #[cfg(all(test,
feature = "test-support"))] mod tests block exercising replay,
TestHarness, TestStore, SpyReactor, and ReactorHarness. examples/README.md
indexes the three; the repo-root README.md links to it.

CI now runs every example in a dedicated matrix job and includes
--all-targets so example test modules execute under cargo nextest.
0xgleb added a commit that referenced this pull request May 14, 2026
* feat: workspace + copy sqlite-es and event-sorcery crates

Crates copied verbatim from st0x.issuance (sqlite-es) and st0x.liquidity (event-sorcery). Adds workspace Cargo.toml with strict lints (warnings/clippy::all/pedantic/nursery/unwrap_used/expect_used denied; unsafe_code forbidden), clippy.toml exempting unwrap/expect in tests, and the canonical events+snapshots schema migration. Workspace dependency versions audited and bumped to current semver-compatible releases. cqrs-es held at 0.4.12 — 0.5.0 is a major bump pending deliberate upgrade.

* feat: AGENTS.md, README, and docs (cqrs, sqlx, ttdd) (#4)

* feat: AGENTS.md, SPEC.md, README, and docs

AGENTS.md adapted from upstream st0x conventions, trimmed to the rules that apply here (drops alloy/brokerage/onchain bits). SPEC.md describes the library's goals, components, and behavior. docs/domain.md is the CQRS/ES glossary and naming conventions. docs/cqrs.md is a usage reference. docs/sqlx.md is the running pitfall log. docs/ttdd.md is the type-driven TDD methodology. CLAUDE.md is a symlink to AGENTS.md so both names resolve to the same source of truth.

* feat: GitHub Actions CI (#5)

* feat: GitHub Actions CI

Matrix runs cargo check / nextest / clippy / fmt --check, plus a separate job for pre-commit hooks. Every step uses 'nix develop -c …' so CI runs the same toolchain as local dev. Caches ~/.cargo and target/ keyed on lockfile + flake.lock.

* feat: replace SqliteProjectionRepo with RepoFamily GAT (#6)

* feat: replace SqliteProjectionRepo with ViewBackend GAT

The previous design parameterized Projection over a Repo type that was bound to ViewRepository<Lifecycle<Entity>, Lifecycle<Entity>>. That bound named the pub(crate) Lifecycle type in a public position, tripping the private_bounds lint, and was suppressed with #[allow(private_bounds)] in three impl blocks pending the crate's extraction.

ViewBackend is a GAT-on-trait HKT emulation: a ViewBackend instance is a type-level function (View, Aggregate) -> SomeRepo. Projection<Entity, Backend: ViewBackend> applies the function internally to obtain Backend::Repo<Lifecycle<Entity>, Lifecycle<Entity>>, so Lifecycle saturation happens inside the struct/method bodies, not in any public bound. SqliteViewBackend is the default. The three #[allow(private_bounds)] suppressions are removed. Test repos InMemoryRepo<View, Agg> and ConflictingRepo<View, Agg> made generic with paired ViewBackend adapters so existing tests still compile.

* docs: add examples of how to use the repo (#7)

Adds a runnable examples/ directory at the workspace root covering the
core event-sorcery surface:

- basic_entity: smallest setup -- EventSourced, Store, send/load,
  load_entity, send_command, load_all_ids, count_aggregates,
  load_ids_paginated, compact_events, incremental_vacuum.
- projection: Materialized = Table with a SQLite generated column for
  filtered queries; load/load_all/filter/rebuild/rebuild_all; injects a
  domain Service via Arc<dyn Clock>.
- reactor: multi-entity StockAlert reactor wired across two stores plus
  a single-entity AuditLog reactor running alongside an auto-projection.

Each example has a README.md explaining the concept and a #[cfg(all(test,
feature = "test-support"))] mod tests block exercising replay,
TestHarness, TestStore, SpyReactor, and ReactorHarness. examples/README.md
indexes the three; the repo-root README.md links to it.

CI now runs every example in a dedicated matrix job and includes
--all-targets so example test modules execute under cargo nextest.
0xgleb added a commit that referenced this pull request May 14, 2026
* feat: nix dev shell + direnv

Rainix-based nix flake providing the rust toolchain, sqlx-cli, cargo-expand, cargo-nextest, and the pre-commit hooks listed in .pre-commit-config.yaml. .envrc auto-loads the shell via nix-direnv. flake.lock pins all inputs for reproducibility.

* feat: copy sqlite-es and event-sorcery crates (#3)

* feat: workspace + copy sqlite-es and event-sorcery crates

Crates copied verbatim from st0x.issuance (sqlite-es) and st0x.liquidity (event-sorcery). Adds workspace Cargo.toml with strict lints (warnings/clippy::all/pedantic/nursery/unwrap_used/expect_used denied; unsafe_code forbidden), clippy.toml exempting unwrap/expect in tests, and the canonical events+snapshots schema migration. Workspace dependency versions audited and bumped to current semver-compatible releases. cqrs-es held at 0.4.12 — 0.5.0 is a major bump pending deliberate upgrade.

* feat: AGENTS.md, README, and docs (cqrs, sqlx, ttdd) (#4)

* feat: AGENTS.md, SPEC.md, README, and docs

AGENTS.md adapted from upstream st0x conventions, trimmed to the rules that apply here (drops alloy/brokerage/onchain bits). SPEC.md describes the library's goals, components, and behavior. docs/domain.md is the CQRS/ES glossary and naming conventions. docs/cqrs.md is a usage reference. docs/sqlx.md is the running pitfall log. docs/ttdd.md is the type-driven TDD methodology. CLAUDE.md is a symlink to AGENTS.md so both names resolve to the same source of truth.

* feat: GitHub Actions CI (#5)

* feat: GitHub Actions CI

Matrix runs cargo check / nextest / clippy / fmt --check, plus a separate job for pre-commit hooks. Every step uses 'nix develop -c …' so CI runs the same toolchain as local dev. Caches ~/.cargo and target/ keyed on lockfile + flake.lock.

* feat: replace SqliteProjectionRepo with RepoFamily GAT (#6)

* feat: replace SqliteProjectionRepo with ViewBackend GAT

The previous design parameterized Projection over a Repo type that was bound to ViewRepository<Lifecycle<Entity>, Lifecycle<Entity>>. That bound named the pub(crate) Lifecycle type in a public position, tripping the private_bounds lint, and was suppressed with #[allow(private_bounds)] in three impl blocks pending the crate's extraction.

ViewBackend is a GAT-on-trait HKT emulation: a ViewBackend instance is a type-level function (View, Aggregate) -> SomeRepo. Projection<Entity, Backend: ViewBackend> applies the function internally to obtain Backend::Repo<Lifecycle<Entity>, Lifecycle<Entity>>, so Lifecycle saturation happens inside the struct/method bodies, not in any public bound. SqliteViewBackend is the default. The three #[allow(private_bounds)] suppressions are removed. Test repos InMemoryRepo<View, Agg> and ConflictingRepo<View, Agg> made generic with paired ViewBackend adapters so existing tests still compile.

* docs: add examples of how to use the repo (#7)

Adds a runnable examples/ directory at the workspace root covering the
core event-sorcery surface:

- basic_entity: smallest setup -- EventSourced, Store, send/load,
  load_entity, send_command, load_all_ids, count_aggregates,
  load_ids_paginated, compact_events, incremental_vacuum.
- projection: Materialized = Table with a SQLite generated column for
  filtered queries; load/load_all/filter/rebuild/rebuild_all; injects a
  domain Service via Arc<dyn Clock>.
- reactor: multi-entity StockAlert reactor wired across two stores plus
  a single-entity AuditLog reactor running alongside an auto-projection.

Each example has a README.md explaining the concept and a #[cfg(all(test,
feature = "test-support"))] mod tests block exercising replay,
TestHarness, TestStore, SpyReactor, and ReactorHarness. examples/README.md
indexes the three; the repo-root README.md links to it.

CI now runs every example in a dedicated matrix job and includes
--all-targets so example test modules execute under cargo nextest.
0xgleb added a commit that referenced this pull request May 14, 2026
* chore: bootstrap repo with LICENSE and shared config

MIT LICENSE plus repo-wide configuration that applies to every PR in the stack: .gitignore, .yamlfmt (formatter config), and .coderabbit.yaml (review automation).

* feat: nix flake (#2)

* feat: nix dev shell + direnv

Rainix-based nix flake providing the rust toolchain, sqlx-cli, cargo-expand, cargo-nextest, and the pre-commit hooks listed in .pre-commit-config.yaml. .envrc auto-loads the shell via nix-direnv. flake.lock pins all inputs for reproducibility.

* feat: copy sqlite-es and event-sorcery crates (#3)

* feat: workspace + copy sqlite-es and event-sorcery crates

Crates copied verbatim from st0x.issuance (sqlite-es) and st0x.liquidity (event-sorcery). Adds workspace Cargo.toml with strict lints (warnings/clippy::all/pedantic/nursery/unwrap_used/expect_used denied; unsafe_code forbidden), clippy.toml exempting unwrap/expect in tests, and the canonical events+snapshots schema migration. Workspace dependency versions audited and bumped to current semver-compatible releases. cqrs-es held at 0.4.12 — 0.5.0 is a major bump pending deliberate upgrade.

* feat: AGENTS.md, README, and docs (cqrs, sqlx, ttdd) (#4)

* feat: AGENTS.md, SPEC.md, README, and docs

AGENTS.md adapted from upstream st0x conventions, trimmed to the rules that apply here (drops alloy/brokerage/onchain bits). SPEC.md describes the library's goals, components, and behavior. docs/domain.md is the CQRS/ES glossary and naming conventions. docs/cqrs.md is a usage reference. docs/sqlx.md is the running pitfall log. docs/ttdd.md is the type-driven TDD methodology. CLAUDE.md is a symlink to AGENTS.md so both names resolve to the same source of truth.

* feat: GitHub Actions CI (#5)

* feat: GitHub Actions CI

Matrix runs cargo check / nextest / clippy / fmt --check, plus a separate job for pre-commit hooks. Every step uses 'nix develop -c …' so CI runs the same toolchain as local dev. Caches ~/.cargo and target/ keyed on lockfile + flake.lock.

* feat: replace SqliteProjectionRepo with RepoFamily GAT (#6)

* feat: replace SqliteProjectionRepo with ViewBackend GAT

The previous design parameterized Projection over a Repo type that was bound to ViewRepository<Lifecycle<Entity>, Lifecycle<Entity>>. That bound named the pub(crate) Lifecycle type in a public position, tripping the private_bounds lint, and was suppressed with #[allow(private_bounds)] in three impl blocks pending the crate's extraction.

ViewBackend is a GAT-on-trait HKT emulation: a ViewBackend instance is a type-level function (View, Aggregate) -> SomeRepo. Projection<Entity, Backend: ViewBackend> applies the function internally to obtain Backend::Repo<Lifecycle<Entity>, Lifecycle<Entity>>, so Lifecycle saturation happens inside the struct/method bodies, not in any public bound. SqliteViewBackend is the default. The three #[allow(private_bounds)] suppressions are removed. Test repos InMemoryRepo<View, Agg> and ConflictingRepo<View, Agg> made generic with paired ViewBackend adapters so existing tests still compile.

* docs: add examples of how to use the repo (#7)

Adds a runnable examples/ directory at the workspace root covering the
core event-sorcery surface:

- basic_entity: smallest setup -- EventSourced, Store, send/load,
  load_entity, send_command, load_all_ids, count_aggregates,
  load_ids_paginated, compact_events, incremental_vacuum.
- projection: Materialized = Table with a SQLite generated column for
  filtered queries; load/load_all/filter/rebuild/rebuild_all; injects a
  domain Service via Arc<dyn Clock>.
- reactor: multi-entity StockAlert reactor wired across two stores plus
  a single-entity AuditLog reactor running alongside an auto-projection.

Each example has a README.md explaining the concept and a #[cfg(all(test,
feature = "test-support"))] mod tests block exercising replay,
TestHarness, TestStore, SpyReactor, and ReactorHarness. examples/README.md
indexes the three; the repo-root README.md links to it.

CI now runs every example in a dedicated matrix job and includes
--all-targets so example test modules execute under cargo nextest.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants