replace tokio::broadcast event bus with SQLite-backed event queue (ADR-0003)

- DomainEvent: add Serialize/Deserialize
- EventConsumer port: poll_next/ack/nack replacing recv()
- EventEnvelope: wraps event with id/retry_count/created_at
- SqliteEventPublisher/SqliteEventConsumer: INSERT/poll/DLQ
- migration: event_queue + dead_letter_queue tables
- InMemoryEventBus: combined publisher+consumer test double
- NoopEventConsumer: test stub
- webhook_consumer: polls EventConsumer instead of broadcast::Receiver
- presentation+mcp wiring: create from SqlitePool
This commit is contained in:
2026-07-12 07:23:21 +02:00
parent e2393be635
commit 826e824b58
13 changed files with 380 additions and 55 deletions

View File

@@ -17,7 +17,6 @@ use infra_wiring::{Config, ConfigSource, DbPool};
use crate::state::AppState;
const EVENT_BUS_CAPACITY: usize = 64;
const DEV_JWT_SECRET: &str = "k-template-dev-secret-not-for-production-use-only";
pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
@@ -29,8 +28,14 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
let auth_service: Arc<dyn domain::ports::AuthService> =
Arc::new(adapter_auth::PasswordAuthService);
let event_bus = Arc::new(adapter_event_publisher::ChannelEventBus::new(EVENT_BUS_CAPACITY));
let event_publisher: Arc<dyn domain::ports::EventPublisher> = event_bus.clone();
let sqlite_pool = match &pool {
#[cfg(feature = "sqlite")]
DbPool::Sqlite(p) => p.clone(),
};
let event_publisher: Arc<dyn domain::ports::EventPublisher> =
Arc::new(adapter_event_publisher::SqliteEventPublisher::new(sqlite_pool.clone()));
let event_consumer: Arc<dyn domain::ports::EventConsumer> =
Arc::new(adapter_event_publisher::SqliteEventConsumer::new(sqlite_pool));
let provider_registry = build_provider_registry(&config).await;
@@ -134,10 +139,10 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
bg_event_publisher,
));
let webhook_rx = event_bus.subscriber();
let webhook_consumer = event_consumer.clone();
let webhook_channel_query = wire_output.channel_query.clone();
tokio::spawn(crate::background::webhook_consumer::run(
webhook_rx,
webhook_consumer,
webhook_channel_query,
reqwest::Client::new(),
));
@@ -172,7 +177,8 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
#[cfg(feature = "auth-jwt")]
jwt_validator,
_library_sync: library_sync,
_event_bus: event_bus,
_event_publisher: event_publisher,
_event_consumer: event_consumer,
config: config_arc,
_sync_trigger: sync_tx,
})