Repository navigation
Description
🤖 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
- Runtime owns transaction creation and lifecycle.
store::replaceableowns coordinate construction, advisory locking, stale-write/tie rules, replacement SQL, mention indexing, and persistence tests.buzz-relay::command_executorstarts 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-storeimmediately - Creating a crate for every domain
- Moving every
Dbmethod in one PR - Converting every
&PgPoolfunction toPgExecutor - Introducing broad
EventStoreorChannelStoretraits - Exposing raw pools to other crates
- Changing replacement semantics
- Moving domain policy into the persistence layer
Relationship to existing issues
- buzz-db: break up lib.rs — move impl Db methods to domain modules #2: Remains the incremental structural tracker.
- buzz-db: unify pool-or-transaction parameter pattern (PgExecutor) #3: Narrows to transaction composition required by this proving slice.
- buzz-db: consolidate command_executor.rs direct SQL into buzz-db #4: Supplies the main correctness outcome: remove duplicated relay SQL.
- buzz-db: extract replaceable-event logic from lib.rs #6: Establishes the first store-oriented domain module.
- buzz-db: introduce domain-scoped traits for testability #8: Is not required; traits remain demand-driven and consumer-owned.
- buzz-db: move lib.rs tests (~4,000 lines) to domain modules #17/buzz-db: consistent #[datastore_span] tracing placement #19: Tests and datastore spans move with each later store extraction.
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
Centralize replaceable event persistence (#6660)
Extract community persistence (#6668)
TheSentinel454 commentedon Aug 25, 2026
🤖 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.
Extract community persistence (#6668)
Extract community persistence (#6668)
Centralize replaceable event persistence (block#6660)
Extract community persistence (block#6668)