Skip to content

Extract MutationEngine and TableBinding abstractions#201

Merged
em3s merged 1 commit intomainfrom
refactor/extract-mutation-engine-abstraction
Feb 24, 2026
Merged

Extract MutationEngine and TableBinding abstractions#201
em3s merged 1 commit intomainfrom
refactor/extract-mutation-engine-abstraction

Conversation

@em3s
Copy link
Copy Markdown
Contributor

@em3s em3s commented Feb 23, 2026

Summary

Extract a V3 engine abstraction layer (MutationService, MutationEngine, TableBinding) between the V3 API and V2 engine internals (Graph, Label, Wal, Cdc). This enables swapping V3 engine implementations without modifying core mutation logic.

Closes #200

Before

Controller → V3MutationService → Graph/Label/Wal/Cdc (direct V2 calls)
  • V3MutationService was a 298-line God class: Edge/MultiEdge branching, V2 type conversion, WAL/CDC, error handling all mixed together
  • EdgeMutationStatus, MultiEdgeMutationStatus, MutationStatus — 3 redundant result types
  • MutationEvent<K> generic — Edge and MultiEdge diverge via separate type parameters

After

Controller → MutationService → MutationEngine(interface) → V2BackedEngine → Graph/Label/Wal/Cdc
                                                         → V3Engine (planned)
  • MutationService (97 lines): generic mutation orchestration, no Edge/MultiEdge distinction
  • MutationEngine + TableBinding: interfaces with no V2 dependencies
  • V2BackedEngine, V2BackedTableBinding, V2BackedMessageBinding: V2 implementations
  • MutationResult, MutationKey: unified result/key types

Changes

  • Add MutationEngine interface: storage/messaging abstraction (WAL, CDC, timeout)
  • Add TableBinding interface: per-table mutation capabilities (lock, read, write, error handling)
  • Add MutationService: generic mutation orchestrator handling Edge/MultiEdge uniformly
  • Introduce MutationKey sealed class: unify SourceTarget (Edge) and Id (MultiEdge) into a single key type
  • Introduce MutationResult: consolidate 3 status types into one
  • Add UnresolvedEvent interface: replace MutationEvent.Source<E> generic
  • Add V2BackedEngine, V2BackedTableBinding, V2BackedMessageBinding: V2 implementations of the above abstractions
  • Extract MutationModeContext: isolate mutation mode decision logic
  • Move EdgeRecordMapper and lockTimeout to GraphDefaults for constructor injection

Removed

  • V3MutationServiceMutationService
  • V3CompatibleTableBindingV2BackedTableBinding
  • EdgeMutationStatus, MultiEdgeMutationStatus, MutationStatusMutationResult

Reviewer Notes

  • No behavioral changes: only the controller-level API signature changes to MutationResult; actual mutation/messaging behavior is identical
  • Suggested review order: MutationEngineTableBindingMutationService → V2 implementations (V2BackedEngineV2BackedTableBindingV2BackedMessageBinding) → verify deleted files

Checklist

  • Small, focused PR (under 500 lines excluding tests); or Draft with a split plan
  • Related issue linked or context added in description
  • Self-reviewed the diff; commented on tricky parts
  • Tests added/updated and passing locally
  • Build and lint checks run
  • Breaking changes documented with migration notes
  • Performance impact reviewed; benchmarks added if needed

Decouple mutation orchestration from V2 engine internals by introducing
clean abstraction layers. This enables pluggable storage backends (e.g.
SlateDB) without touching core mutation logic.

Key changes:
- Add MutationEngine interface for storage/messaging abstraction
- Add TableBinding interface for per-table mutation capabilities
- Add generic MutationService that handles Edge/MultiEdge uniformly
- Replace V3MutationService with generic MutationService
- Replace V3CompatibleTableBinding with V2BackedTableBinding
- Unify EdgeMutationStatus/MultiEdgeMutationStatus into MutationResult
- Unify MutationEvent<K> generic into sealed MutationKey type
- Extract MutationMode and MutationModeContext for mode decisions
- Add V2BackedEngine and V2BackedMessageBinding as V2 adapters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Feb 23, 2026
@em3s em3s changed the title refactor(engine): extract MutationEngine and TableBinding abstractions extract MutationEngine and TableBinding abstractions Feb 23, 2026
@em3s em3s changed the title extract MutationEngine and TableBinding abstractions Extract MutationEngine and TableBinding abstractions Feb 24, 2026
@em3s em3s merged commit 66aa29f into main Feb 24, 2026
8 checks passed
@em3s em3s deleted the refactor/extract-mutation-engine-abstraction branch March 1, 2026 12:58
em3s added a commit that referenced this pull request Mar 25, 2026
Bytecode-level static analysis tool (ASM) that finds V3→V2 direct calls
bypassing the adapter layer. Validates decoupling progress from #200/#201.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
em3s added a commit that referenced this pull request Mar 25, 2026
…l detection

Instead of classifying all 134 packages into v3/adapter/v2/shared,
detect leaks by finding all callers of Graph class and applying
exclude/include rules:

1. Class exclude (V2Backed*) — adapter classes always OK
2. Package include (v2.engine.v3) — overrides exclude for monitoring
3. Package exclude (v2.engine, server.api.graph.v2) — V2 internals OK
4. Everything else calling Graph = leak

Config reduced from 208 lines to 38 lines. Validated against pre-#201
code: correctly detects V3MutationService as leak (6 edges).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove direct V2 engine calls from V3 mutation path

1 participant