- Update architecture docs to reflect pivot from @libsql/client to Honker - Fold @alkdev/drizzlebox Phase 0 into src/sqlite/utils/ (ADR-046) - Add HonkerEventTarget adapter for pubsub TypedEventTarget (ADR-047) - Replace hand-written CRUD with OperationSpec generation (ADR-048) - Resolved OQ-26: Honker replaces Redis for single-node pub/sub (POC validated) - Updated OQ-17, OQ-18, OQ-19 for OperationSpec repository surface - Added OQ-30 (composite event target), OQ-31 (consumer naming), OQ-32 (Drizzle Kit) - POC results: adapter buildable, same-process pub/sub works, transactional outbox semantics confirmed, concurrent listeners/streams work - Research doc at docs/research/pivot-honker-sqlite-adapter.md
74 lines
3.1 KiB
Markdown
74 lines
3.1 KiB
Markdown
---
|
|
status: accepted
|
|
date: 2026-06-01
|
|
supersedes: ADR-033 (partially — OQ-17, OQ-18, OQ-19 updated)
|
|
---
|
|
|
|
# ADR-048: OperationSpecs as Repository Surface
|
|
|
|
## Context
|
|
|
|
ADR-033 established JSON path queries with hand-written CRUD for v1. The
|
|
question was whether the repository layer would be hand-written query
|
|
functions, auto-generated from table definitions, or something else.
|
|
|
|
The `@alkdev/operations` package provides `OperationSpec` — a serializable
|
|
descriptor for a query, mutation, or subscription operation. Hubs and spokes
|
|
register specs in an `OperationRegistry` along with handlers. The operations
|
|
runtime handles execution, call protocol routing, subscriptions, access
|
|
control, and validation.
|
|
|
|
This presents a natural alternative to hand-written CRUD: storage outputs
|
|
`OperationSpec[]` describing CRUD operations per table, and the consumer
|
|
wires them into the operations registry.
|
|
|
|
## Decision
|
|
|
|
Storage does not ship a "repository layer" of hand-written query functions.
|
|
Instead, it outputs `OperationSpec[]` per table — flat arrays describing
|
|
CRUD operations. The consumer (hub/spoke) imports these specs, registers
|
|
handlers, and the operations runtime handles execution.
|
|
|
|
```ts
|
|
// Storage defines table + operation contracts
|
|
export const graphTypes = sqliteTable("graph_types", { ... });
|
|
export const graphTypeSpecs: OperationSpec[] = [
|
|
{ name: "create", namespace: "graph_types", type: "mutation", ... },
|
|
{ name: "find", namespace: "graph_types", type: "query", ... },
|
|
{ name: "list", namespace: "graph_types", type: "query", ... },
|
|
{ name: "update", namespace: "graph_types", type: "mutation", ... },
|
|
{ name: "delete", namespace: "graph_types", type: "mutation", ... },
|
|
];
|
|
|
|
// Hub registers specs + handlers
|
|
for (const spec of graphTypeSpecs) {
|
|
registry.registerSpec(spec);
|
|
registry.registerHandler(`${spec.namespace}.${spec.name}`, handler);
|
|
}
|
|
```
|
|
|
|
The handler is consumer-provided. Storage defines the contract (input/output
|
|
schemas, operation type); the hub provides the execution (Drizzle queries,
|
|
Honker transactions, notifications).
|
|
|
|
`@alkdev/operations` is a type-only peer dependency of storage. No circular
|
|
dependency.
|
|
|
|
## Consequences
|
|
|
|
- **No hand-written repository functions** — storage avoids the maintenance
|
|
burden of typed CRUD code. The contract is the OperationSpec.
|
|
- **Consumer owns execution** — the hub decides how to handle each operation
|
|
(raw Drizzle query, with Honker notification, with access control, etc.).
|
|
Storage doesn't execute queries.
|
|
- **Subscriptions for free** — OperationSpec supports `type: "subscription"`.
|
|
Table-level change streams become operation subscriptions rather than
|
|
custom event handling.
|
|
- **Clean dependency graph** — storage depends on operations only for the
|
|
`OperationSpec` type (peer dep). No runtime dependency.
|
|
- **Attribute queries remain JSON path** — for metagraph tables where
|
|
attributes are dynamic JSON, `json_extract()` is still the query mechanism.
|
|
Domain-specific tables with native columns produce simpler specs.
|
|
- **Supersedes ADR-033 partially** — the "hand-written CRUD" part is replaced.
|
|
The "JSON path queries for attributes" part still applies to metagraph
|
|
tables. |