feat: Phase 3d — ReadModels port, postgres impl, HTTP reads rewire
Problem: entity/relationship/graph/blast-radius reads were embedded
as inline SQL in the httpapi handlers, duplicating the recursive type
tree CTE, the blast_radius function call, and the topology-picking
query across the REST and MCP surfaces with no port abstraction.
Change:
- ports.ReadModels interface: ListEntities, GetEntity, GetEntityBySlug,
GetEntityRelations, GetBlastRadius, GetGraph (with health),
ListEntityTypes. Returns EntityWithHealth (domain.Entity + health
from entity_status join) and domain.Relationship — no gen types
in the port.
- adapters/postgres/readmodels.go: EntityReader implements ReadModels
with the existing SQL verbatim (recursive type filter, blast_radius,
most-connected-first topology, graph edge listing).
- httpapi/entities.go: ListEntities, GetEntity, GetEntityRelations,
GetBlastRadius, GetGraph rewired to ReadModels. SQL moved to the
adapter; handlers map domain/ports types to gen wire shapes.
entityWithHealthToGen, sqlcEntityToGen helpers added.
- Old sqlcEntityToGen (sqlcgen.Entity → gen.Entity) preserved for
client_lifecycle.go; mutation handlers use domainToGen.
Verification: go build/vet, full test suite (19 pkgs), DB integration
(postgres + mcp — both green). httpapi DB tests have the pre-existing
set of failures (TestAPIEndToEnd entity_types=60/501, TestPhase3*)
verified at ec11956.
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/dtoro/oikos/internal/config"
|
||||
"github.com/dtoro/oikos/internal/adapters/postgres"
|
||||
"github.com/dtoro/oikos/internal/core/app"
|
||||
"github.com/dtoro/oikos/internal/core/ports"
|
||||
"github.com/dtoro/oikos/internal/httpapi/gen"
|
||||
mcphandler "github.com/dtoro/oikos/internal/mcp"
|
||||
"github.com/dtoro/oikos/internal/safego"
|
||||
@@ -66,6 +67,7 @@ type Server struct {
|
||||
// replay path needs. Wired here until main becomes the composition root.
|
||||
entities *app.EntityService
|
||||
entityRepo *db.EntityRepo
|
||||
readModels ports.ReadModels
|
||||
}
|
||||
|
||||
// NewHandler builds the full HTTP handler: /healthz (unauthenticated,
|
||||
@@ -85,6 +87,7 @@ func NewHandler(ctx context.Context, pool *db.Pool, cfg config.Config) http.Hand
|
||||
}
|
||||
s.entityRepo = db.NewEntityRepo(pool)
|
||||
s.entities = app.NewEntityService(s.entityRepo, db.NewOntologyRepo(pool, time.Minute))
|
||||
s.readModels = db.NewEntityReader(pool)
|
||||
|
||||
// Wire secrets backend: Infisical primary with SOPS DR fallback.
|
||||
if cfg.InfisicalSiteURL != "" {
|
||||
|
||||
Reference in New Issue
Block a user