feat(eventcore-demo): demo application with PostgreSQL backend (#260) #412

Merged
jwilger merged 1 commit from feat/260-demo-app into main 2026-06-13 08:34:47 -07:00
Owner

Summary

Adds eventcore-demo, a runnable demonstration application showing EventCore with the PostgreSQL backend. A small bank domain centered on the multi-stream atomic Transfer — EventCore's signature capability: a single execute() atomically withdraws from the source account stream and deposits to the destination stream under optimistic concurrency.

What's included

  • Domain (domain.rs): semantic nutype types (MoneyAmount, AccountHolder), BankEvent, account-id helper.
  • Commands (commands.rs): OpenAccount, Deposit, Withdraw (single-stream) and Transfer (multi-stream atomic) — all #[derive(Command)] + CommandLogic with pure apply/handle, private write-model state with intent-revealing methods, and typed thiserror business-rule errors.
  • Read model (projections.rs): a TransactionHistory projector — a separate code path from command state (CQRS).
  • Binary (main.rs): configures PostgresEventStore from DATABASE_URL (default localhost:5433), migrates, runs open→deposit→transfer, projects, and prints balances + a conserved-total check. No panics; errors map to ExitCode::FAILURE.
  • README: prerequisites, docker-compose up -d (reuses the root compose), and cargo run -p eventcore-demo.
  • Tests: 6 in-memory usage-example tests (happy path, insufficient-funds rejection, multi-stream transfer atomicity, failed-transfer leaves both streams unchanged) + 1 Postgres e2e test. Public API only.

publish = false (demo, excluded from crates.io / release lockstep). Lint block matches the established bin-crate convention (eventcore-stress/eventcore-bench).

Verification

  • cargo clippy --workspace --all-targets --all-features -- -D warnings clean
  • cargo nextest run --workspace — 276 tests pass (7 demo tests)
  • cargo run -p eventcore-demo against Postgres: Alice 1000−300=700, Bob 250+300=550, total conserved at 1250, with both transfer legs visible in the log.

Acceptance criteria

  • Compiles and runs
  • README with setup instructions
  • 2-3 aggregate types (accounts + transaction-history read model)
  • Projection / read-model patterns
  • PostgreSQL event store configuration
  • Docker Compose setup (root compose, documented)
  • Integration tests as usage examples

Closes #260

## Summary Adds `eventcore-demo`, a runnable demonstration application showing EventCore with the PostgreSQL backend. A small bank domain centered on the **multi-stream atomic `Transfer`** — EventCore's signature capability: a single `execute()` atomically withdraws from the source account stream and deposits to the destination stream under optimistic concurrency. ## What's included - **Domain** (`domain.rs`): semantic `nutype` types (`MoneyAmount`, `AccountHolder`), `BankEvent`, account-id helper. - **Commands** (`commands.rs`): `OpenAccount`, `Deposit`, `Withdraw` (single-stream) and `Transfer` (multi-stream atomic) — all `#[derive(Command)]` + `CommandLogic` with pure `apply`/`handle`, private write-model state with intent-revealing methods, and typed `thiserror` business-rule errors. - **Read model** (`projections.rs`): a `TransactionHistory` projector — a separate code path from command state (CQRS). - **Binary** (`main.rs`): configures `PostgresEventStore` from `DATABASE_URL` (default `localhost:5433`), migrates, runs open→deposit→transfer, projects, and prints balances + a conserved-total check. No panics; errors map to `ExitCode::FAILURE`. - **README**: prerequisites, `docker-compose up -d` (reuses the root compose), and `cargo run -p eventcore-demo`. - **Tests**: 6 in-memory usage-example tests (happy path, insufficient-funds rejection, multi-stream transfer atomicity, failed-transfer leaves both streams unchanged) + 1 Postgres e2e test. Public API only. `publish = false` (demo, excluded from crates.io / release lockstep). Lint block matches the established bin-crate convention (`eventcore-stress`/`eventcore-bench`). ## Verification - `cargo clippy --workspace --all-targets --all-features -- -D warnings` clean - `cargo nextest run --workspace` — 276 tests pass (7 demo tests) - `cargo run -p eventcore-demo` against Postgres: Alice 1000−300=700, Bob 250+300=550, total conserved at 1250, with both transfer legs visible in the log. ## Acceptance criteria - [x] Compiles and runs - [x] README with setup instructions - [x] 2-3 aggregate types (accounts + transaction-history read model) - [x] Projection / read-model patterns - [x] PostgreSQL event store configuration - [x] Docker Compose setup (root compose, documented) - [x] Integration tests as usage examples Closes #260
feat(eventcore-demo): demo application with PostgreSQL backend (#260)
All checks were successful
CI / Request auto_review semantic review (pull_request) Successful in 1s
CI / Test (pull_request) Successful in 3m25s
CI / Detect Changes (pull_request) Successful in 3s
CI / Format (pull_request) Successful in 14s
auto_review auto_review: no findings
CI / Mutation (pull_request) Has been skipped
CI / CI Gate (pull_request) Successful in 2s
CI / Clippy (pull_request) Successful in 2m17s
CI / Security Audit (pull_request) Successful in 27s
35fbf779b5
Add a runnable demonstration crate showcasing EventCore with the
PostgreSQL backend. The domain models a bank where each account is its
own event stream.

- Commands (write model): OpenAccount, Deposit, Withdraw — each a single
  stream — plus Transfer, the centerpiece, which declares two #[stream]
  fields and atomically emits a withdrawal on the source stream and a
  deposit on the destination stream in one execute() call, demonstrating
  multi-stream atomic writes with optimistic concurrency.
- Read model (projection): TransactionHistoryProjector folds events into
  a TransactionHistory (per-account balances + ordered log) via
  run_projection, on a code path fully separate from the command write
  models (CQRS separation).
- Semantic types via nutype: MoneyAmount (> 0 cents), AccountHolder
  (trimmed, non-empty); accounts identified by StreamId.
- Binary (src/main.rs) wires a PostgresEventStore from DATABASE_URL
  (default localhost:5433), runs the idempotent migrate(), executes a
  scripted open/deposit/transfer scenario, projects, and prints balances
  and the transaction log. Errors propagate to a clean process exit.
- Integration tests exercise only the public API (execute,
  run_projection, derive macro, Projector). bank_account_test covers the
  happy path, insufficient-funds and unopened-account rejections, the
  multi-stream Transfer, and that a failed transfer leaves both streams
  unchanged. postgres_e2e_test proves the same flow against PostgreSQL.
- README documents reusing the root docker-compose Postgres (:5433) and
  how to run the demo and tests.

The crate sets publish = false and uses a standalone lints block (like
the sibling binary crates) to avoid the binary-test-profile dead_code
false positive while keeping the code free of real violations.

Phases 1-3 were effectively covered by the issue's detailed acceptance
criteria and the existing example idioms; this is the Phase 4
implementation.

Closes #260
auto-review left a comment

This pull request introduces a new demo application, eventcore-demo, showcasing EventCore with a PostgreSQL backend. The demo includes a small bank domain with commands for account operations and a multi-stream atomic transfer feature. The implementation appears robust with comprehensive tests and documentation, making it safe to merge.

Walkthrough

  • Cargo.toml and Cargo.lock: Added eventcore-demo as a new package in the workspace.
  • eventcore-demo/Cargo.toml: Configured dependencies and lints, with publish = false to exclude from crates.io.
  • README.md: Detailed setup and execution instructions for the demo.
  • Domain and Commands: Defined domain types and commands for bank operations, emphasizing CQRS separation.
  • Main.rs: Implements the demo scenario, connecting to PostgreSQL and executing account operations.
  • Projections: Implements a read model for transaction history, separate from the command write models.
  • Tests: Comprehensive tests for both in-memory and PostgreSQL backends, ensuring correctness and atomicity of operations.

LLM usage and cost

This pull request introduces a new demo application, `eventcore-demo`, showcasing EventCore with a PostgreSQL backend. The demo includes a small bank domain with commands for account operations and a multi-stream atomic transfer feature. The implementation appears robust with comprehensive tests and documentation, making it safe to merge. ## Walkthrough - **Cargo.toml and Cargo.lock**: Added `eventcore-demo` as a new package in the workspace. - **eventcore-demo/Cargo.toml**: Configured dependencies and lints, with `publish = false` to exclude from crates.io. - **README.md**: Detailed setup and execution instructions for the demo. - **Domain and Commands**: Defined domain types and commands for bank operations, emphasizing CQRS separation. - **Main.rs**: Implements the demo scenario, connecting to PostgreSQL and executing account operations. - **Projections**: Implements a read model for transaction history, separate from the command write models. - **Tests**: Comprehensive tests for both in-memory and PostgreSQL backends, ensuring correctness and atomicity of operations. ## LLM usage and cost - Reasoning (gpt-4o) in=13444 out=347 cost=$0.072425 - Cheap (gpt-4o-mini) in=845 out=48 cost=$0.000156 Estimated total USD: $0.072581 via https://api.openai.com and https://api.openai.com
jwilger deleted branch feat/260-demo-app 2026-06-13 08:34:48 -07:00
Sign in to join this conversation.
No description provided.