Skip to content

buzz-db: separate database runtime infrastructure from domain store modules #34

Closed

Description

@TheSentinel454

🤖 This issue was drafted and filed by an AI agent from an engineering handoff.

Decision

Keep database runtime infrastructure and Buzz domain persistence in the existing buzz-db crate for now, but establish a clear internal ownership boundary between them.

Do not create a buzz-store crate yet. Use event replacement as the proving slice.

Goal

Make Postgres access consistent and make domain persistence easier to locate, test, and change without a flag-day reorganization.

buzz-db currently owns two distinct responsibilities:

Responsibility Examples
Database runtime Pool construction/configuration, writer/read-replica routing, transactions, connection/session invariants, acquisition/transaction metrics, health/readiness support
Domain persistence Event replacement, channels/membership, communities, workflows, reminders, row mapping, and domain-specific SQL

The ownership rule should be:

  • Runtime: How is Postgres acquired and used safely?
  • Store: How is Buzz domain state persisted?
  • Core: What do Buzz types and pure domain rules mean?
  • Relay: What operation should happen for an incoming event or request?

This is analogous to an API/domain/store separation while retaining one crate until the boundary has been proven across multiple domains.

Possible internal structure

The exact folders are not the requirement, but a possible destination is:

buzz-db/src/
  runtime/
    pool.rs
    transaction.rs
    read_session.rs
    metrics.rs

  store/
    event.rs
    replaceable.rs
    channel.rs
    community.rs
    workflow.rs
    reminder.rs

  migration/
  lib.rs

Initially, store-oriented methods can remain impl Db methods physically located in their domain modules. This issue does not require domain handles such as db.events().

First proving slice

Use #3, #4, and #6 together:

  • Runtime owns transaction creation and lifecycle.
  • store::replaceable owns coordinate construction, advisory locking, stale-write/tie rules, replacement SQL, mention indexing, and persistence tests.
  • buzz-relay::command_executor starts or participates in its transaction and calls the store operation instead of duplicating SQL.

The semantic API should require a transaction:

pub async fn replace_parameterized_event_in_transaction(
    &self,
    tx: &mut Transaction<'_, Postgres>,
    event: &Event,
) -> Result<ReplaceOutcome>;

A transaction-required operation should accept Transaction, not a generic PgConnection. Private SQL primitives may accept PgConnection when they are genuinely valid both inside and outside a transaction.

Acceptance criteria

  • Runtime and domain-store responsibilities are documented.
  • Replacement persistence lives in a store-oriented module.
  • The relay no longer contains duplicate replacement SQL.
  • Transaction-required APIs express that requirement in their types.
  • No raw writer/read pool accessor is added for consumers.
  • Existing public behavior and replacement semantics remain unchanged.
  • Tests cover:
    • Concurrent writes to the same coordinate
    • Same-second replacement ties
    • Stale revisions
    • Idempotent replay
    • Transaction rollback
    • Mention-indexing rollback
  • The resulting structure documents a repeatable pattern for later community, channel, and reminder extractions.

Non-goals

  • Creating buzz-store immediately
  • Creating a crate for every domain
  • Moving every Db method in one PR
  • Converting every &PgPool function to PgExecutor
  • Introducing broad EventStore or ChannelStore traits
  • Exposing raw pools to other crates
  • Changing replacement semantics
  • Moving domain policy into the persistence layer

Relationship to existing issues

Longer-term option

If the runtime/store boundary remains stable across several domain extractions, consider moving all store modules into one buzz-store crate:

buzz-relay
    ↓
buzz-store ──→ buzz-core
    ↓
buzz-db

Avoid per-domain store crates unless a domain develops an independent lifecycle or dependency boundary. Cross-domain transactions are substantially easier to compose through one store layer.

If that extraction occurs, schema migrations should likely move with buzz-store, because the store implementation owns the schema contract expected by its queries.

Activity

  1. TheSentinel454 commented on Aug 25, 2026

    @TheSentinel454
    OwnerAuthor

    🤖 Closing as completed by block#6660 and block#6668. The runtime/store ownership rule is documented, parameterized replacement proves the transaction-required store seam, and the community extraction demonstrates the repeatable domain-module pattern.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions