| 02-backend-refactor |
01 |
database |
| golang-migrate |
| sqlite |
| store-interface |
| dependency-injection |
| migrations |
| embed-fs |
|
| phase |
provides |
| 01-data-integrity |
PRAGMA foreign_keys enforcement and UPSERT semantics in existing diunwebhook.go |
|
|
| Store interface with 9 methods covering all persistence operations |
| SQLiteStore implementing Store with exact SQL from current handlers |
| RunMigrations function using golang-migrate + embed.FS (iofs source) |
| Baseline migration 0001 with full current schema (CREATE TABLE IF NOT EXISTS) |
|
| 02-02 (Server struct refactor will use Store interface and RunMigrations) |
| 03-postgresql (PostgreSQLStore will implement same Store interface) |
|
| added |
patterns |
| github.com/golang-migrate/migrate/v4 v4.19.1 |
| github.com/golang-migrate/migrate/v4/database/sqlite (modernc.org/sqlite driver, no CGO) |
| github.com/golang-migrate/migrate/v4/source/iofs (embed.FS migration source) |
|
| Store interface pattern - persistence abstraction hiding *sql.DB from handlers |
| SQLiteStore with per-struct sync.Mutex (replaces package-level global) |
| golang-migrate with embedded SQL files via //go:embed migrations/sqlite |
| ErrNoChange guard in RunMigrations (startup idempotency) |
| CREATE TABLE IF NOT EXISTS in baseline migration (backward compatible with existing databases) |
|
|
| created |
modified |
| pkg/diunwebhook/store.go |
| pkg/diunwebhook/sqlite_store.go |
| pkg/diunwebhook/migrate.go |
| pkg/diunwebhook/migrations/sqlite/0001_initial_schema.up.sql |
| pkg/diunwebhook/migrations/sqlite/0001_initial_schema.down.sql |
|
|
|
| Used database/sqlite sub-package (not database/sqlite3) to avoid CGO - confirmed modernc.org/sqlite usage in sqlite.go source |
| Single 0001 baseline migration with full schema including acknowledged_at - safe for existing databases via CREATE TABLE IF NOT EXISTS |
| NewSQLiteStore sets MaxOpenConns(1) and PRAGMA foreign_keys = ON - moved from InitDB which will be removed in Plan 02 |
| AssignTag preserves INSERT OR REPLACE (not ON CONFLICT DO UPDATE) per research Pitfall 6 - correct semantics for tag_assignments PRIMARY KEY |
| defer rows.Close() directly (not verbose closure pattern) as plan specifies |
|
| Store interface: all persistence behind 9 named methods, no *sql.DB in interface signature |
| SQLiteStore field mutex: sync.Mutex as struct field, not package global - enables parallel test isolation |
| Migration files: versioned SQL files embedded via //go:embed, applied via golang-migrate at startup |
| ErrNoChange is not an error: errors.Is(err, migrate.ErrNoChange) guard ensures idempotent startup |
|
|
6min |
2026-03-23 |