Skip to content

refactor(api): repository layer + minimal domain extraction - #9

Merged
murugu-21 merged 11 commits into
mainfrom
refactor/api-repository-layer
Jun 16, 2026
Merged

murugu-21 merged 11 commits into
mainfrom
refactor/api-repository-layer

Conversation

@murugu-21

@murugu-21 murugu-21 commented Jun 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

Restructures apps/api into clean layers — no behavior change (pure structural refactor verified against the existing integration suite).

Repository layer (ports + Drizzle adapters)

  • Foundation: Executor type + poolExecutor() in @eventform/db — a Drizzle handle that's tx-bound for atomic work or pool-bound for anonymous reads.
  • One port + Drizzle adapter per module: endpoints, submissions, deliveries, forms, public, tenants.
  • drizzle-orm is now imported only in *.repository.drizzle.ts adapters — no service imports it.
  • Pool-only methods (tenant provisioning, anonymous form read) don't take an Executor — honest signatures, no RLS foot-gun.

Minimal domain layer (framework-free, no entities/mappers)

  • apps/api/src/domain/: buildSubmissionReceivedEvent / rebuildRetryEvent (outbox payloads), canRetryDelivery (retry rule), and an EndpointSecret value object.
  • Services call these instead of inlining the rules; they keep owning transactions + HTTP exceptions.

Design decisions

  • Executor pattern, not a UnitOfWork factory — repos take a handle, the service owns transaction policy.
  • Atomicity preserved: public submit (submission + N deliveries + N outbox) and deliveries retry (delivery update + outbox insert) each stay in one withTenant transaction.
  • RLS preserved: tenant-scoped ops run under withTenant (SET LOCAL app.tenant_id); the anonymous published-form read runs on the pool so public-read policies apply.
  • Deliberately scoped: minimal domain = pure functions/VO only. No domain entities, aggregates, or mappers — that ceremony isn't warranted for a domain this thin/stable. Form-state guards and the endpoint cap
    left inline on purpose.

Spec + implementation plan included under docs/superpowers/.

Test plan

  • pnpm --filter @eventform/db test → 12/12
  • pnpm --filter @eventform/api test → 68/68 (62 integration + 6 new pure domain unit tests)
  • Builds clean (db + api); domain layer verified free of NestJS/Drizzle/pg
  • Independent final code review: no critical issues; atomicity + RLS confirmed preserved
  • Playwright browser smoke (recommended pre-merge — not run in this session)

🤖 Generated with Claude Code

murugu-21 and others added 11 commits June 16, 2026 14:11
Repository ports + Drizzle adapters for apps/api (no domain layer yet).
Executor pattern preserves the one-transaction outbox write and RLS scoping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8 tasks: Executor foundation + one repository (port + Drizzle adapter) per
module, services keep withTenant/logic, boundary-verification sweep. Refactor
under the existing green integration suite (no new tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ue object

Extract the genuinely-pure domain bits into a framework-free apps/api/src/domain/
(no NestJS, no Drizzle): buildSubmissionReceivedEvent/rebuildRetryEvent (outbox
payloads), canRetryDelivery (retry rule), and an EndpointSecret value object.
Services call these instead of inlining; they keep owning transactions + HTTP
errors. Adds pure unit tests (nothing to mock). No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@murugu-21 murugu-21 changed the title refactor(api): repository layer — ports + Drizzle adapters refactor(api): repository layer + minimal domain extraction Jun 16, 2026
@murugu-21
murugu-21 merged commit 60aebe7 into main Jun 16, 2026
3 checks passed
@murugu-21
murugu-21 deleted the refactor/api-repository-layer branch October 13, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant