docs(core): add cordis architecture alignment implementation plan

This commit is contained in:
ryan
2026-08-28 13:32:55 +08:00
parent 0bc19e34cb
commit 0b4e928d1a
@@ -0,0 +1,221 @@
# Cordis Architecture Alignment & Refactoring Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Implement Cordis spatiotemporal composability (scoped revertible effects and reactive fiber lifecycle state machine) in `backend/core`, and eliminate cross-plugin direct imports in domain repositories.
**Architecture:**
1. Build scoped extension proxies on `core.Context` that automatically attach unregister callbacks to `ctx.OnDispose` in LIFO order upon registration.
2. Introduce `core/fiber.go` implementing the Fiber state machine (`PENDING -> LOADING -> ACTIVE -> UNLOADING -> DISPOSED`) with a reactive reconciler in `App`/`Container` ensuring dependency confluence.
3. Clean up defensive boundaries in `backend/plugins/domain/user` by removing direct `database.DB(ctx)` imports in favor of `contracts.DBService`.
**Tech Stack:** Go 1.24+, GORM, Gin, Asynq, Cordis micro-kernel paradigm.
## Global Constraints
- Strictly preserve `backend/pkg/util/` purity (no Gin/GORM imports).
- Zero physically hardcoded temp directories in tests (use `t.TempDir()`).
- All Go error returns and logging must adhere to project standards.
- Follow Conventional Commits (`feat(core): ...`, `refactor(user): ...`).
---
### Task 1: Scoped Revertible Effects for Core Extpoints
**Files:**
- Create: `backend/core/scoped_extpoints.go`
- Modify: `backend/core/context.go`
- Modify: `backend/core/extpoints/task.go`
- Modify: `backend/core/extpoints/schedule.go`
- Modify: `backend/core/extpoints/setting.go`
- Test: `backend/core/context_test.go`
**Interfaces:**
- Consumes: `core.Context`, `extpoints.RouterExtension`, `extpoints.TaskExtension`, `extpoints.ScheduleExtension`, `extpoints.SettingExtension`, `core.EventBus`
- Produces: Scoped extension methods on `Context` that automatically register LIFO disposers when routes, tasks, schedules, settings, and events are registered.
- [ ] **Step 1: Write the failing test for scoped extpoints automatic teardown**
In `backend/core/context_test.go`, add test cases verifying that registering routes, tasks, schedules, settings, and event listeners on a child context automatically registers unregister callbacks, and calling `childCtx.Dispose()` completely rolls them back:
```go
func TestContext_ScopedExtpoints_RevertibleEffects(t *testing.T) {
root := NewContext(context.Background())
child := root.Fork()
// Register route, task, schedule, setting, event on child
rd := child.Router().GET("/test-route", func() {})
assert.Equal(t, 1, len(root.Router().Routes()))
child.Events().On("test:event", func() {})
assert.Equal(t, 1, root.Events().Listeners("test:event"))
// Dispose child
err := child.Dispose()
assert.NoError(t, err)
// All child effects should be revoked
assert.Equal(t, 0, len(root.Router().Routes()))
assert.Equal(t, 0, root.Events().Listeners("test:event"))
}
```
- [ ] **Step 2: Run test to verify it fails**
Run: `go test -v ./backend/core -run TestContext_ScopedExtpoints_RevertibleEffects`
Expected: FAIL (because current `Router().GET()` does not bind unregistration to `child.OnDispose`).
- [ ] **Step 3: Implement Scoped Extpoints and Context bindings**
1. In `backend/core/extpoints/task.go`, ensure `Unregister(taskType string) bool` exists.
2. In `backend/core/extpoints/schedule.go`, ensure `Unregister(name string) bool` exists.
3. In `backend/core/extpoints/setting.go`, ensure `Unregister(key string) bool` exists.
4. In `backend/core/scoped_extpoints.go` (or `context.go`), create scoped wrappers for `RouterExtension`, `TaskExtension`, `ScheduleExtension`, `SettingExtension` and `EventBus` that tie registrations to `ctx.OnDispose`.
- [ ] **Step 4: Run tests to verify they pass**
Run: `go test -v ./backend/core -run TestContext_ScopedExtpoints_RevertibleEffects`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add backend/core/
git commit -m "feat(core): implement scoped revertible effects for context extpoints"
```
---
### Task 2: Plugin Fiber State Machine and Reactive Coeffects (Confluence)
**Files:**
- Create: `backend/core/fiber.go`
- Create: `backend/core/fiber_test.go`
- Modify: `backend/core/app.go`
- Modify: `backend/core/types.go`
- Modify: `backend/core/container.go`
**Interfaces:**
- Consumes: `core.Plugin`, `core.Context`, `core.Container`
- Produces: `core.DependentPlugin`, `core.Fiber`, `core.FiberState`, `App.Reconcile()`
- [ ] **Step 1: Write the failing test for Fiber state machine and out-of-order registration confluence**
In `backend/core/fiber_test.go`:
```go
func TestFiber_ConfluenceAndReactiveActivation(t *testing.T) {
app := NewApp()
// Plugin B depends on contracts.DBService, but is registered BEFORE DatabasePlugin (Plugin A)
pluginB := &mockDependentPlugin{
name: "plugin-b",
deps: []reflect.Type{reflect.TypeFor[contracts.DBService]()},
}
pluginA := &mockDBPlugin{name: "database"}
app.Use(pluginB, pluginA)
err := app.Start(context.Background())
assert.NoError(t, err)
// Verify both plugins reached FiberActive state and B executed Apply successfully after A provided DBService
assert.True(t, pluginB.applied)
assert.True(t, pluginA.applied)
_ = app.Stop()
}
```
- [ ] **Step 2: Run test to verify it fails**
Run: `go test -v ./backend/core -run TestFiber_ConfluenceAndReactiveActivation`
Expected: FAIL (because current `app.ApplyPlugins()` applies in static slice order without dependency reconciliation).
- [ ] **Step 3: Implement Fiber State Machine and Reconciler**
1. In `backend/core/types.go`, declare:
```go
type DependentPlugin interface {
Plugin
Inject() []reflect.Type
}
```
2. In `backend/core/fiber.go`, implement `Fiber` with states (`FiberPending`, `FiberLoading`, `FiberActive`, `FiberUnloading`, `FiberDisposed`), child scoped context, and state transition methods.
3. In `backend/core/app.go`, integrate Fibers into `App` and implement iterative dependency reconciliation during `ApplyPlugins` and on dynamic `Provide`.
- [ ] **Step 4: Run tests to verify they pass**
Run: `go test -v ./backend/core`
Expected: ALL PASS
- [ ] **Step 5: Commit**
```bash
git add backend/core/
git commit -m "feat(core): implement plugin fiber state machine and reactive dependency reconciler"
```
---
### Task 3: Domain Plugin Isolation & Boundary Enforcement
**Files:**
- Modify: `backend/plugins/domain/user/repository.go`
- Modify: `backend/plugins/domain/user/service.go`
- Modify: `backend/plugins/domain/user/handlers.go`
- Modify: `backend/plugins/domain/user/plugin.go`
- Test: `backend/plugins/domain/user/plugin_test.go`
**Interfaces:**
- Consumes: `contracts.DBService` via `core.Inject` / `ctx.DB()`
- Produces: Decoupled User repository without direct `Wavelet/plugins/infra/database` imports.
- [ ] **Step 1: Write/update test verifying User repository works with injected DBService**
In `backend/plugins/domain/user/plugin_test.go`, test user CRUD operations resolving `contracts.DBService` through Context.
- [ ] **Step 2: Run test to verify current state**
Run: `go test -v ./backend/plugins/domain/user/...`
- [ ] **Step 3: Refactor user repository to eliminate direct `plugins/infra/database` imports**
In `backend/plugins/domain/user/repository.go`:
- Remove `import "Wavelet/plugins/infra/database"`.
- Obtain `*gorm.DB` via `ctx` (e.g. from context using `contracts.DBService` or context value).
- [ ] **Step 4: Run tests to verify they pass**
Run: `go test -v ./backend/plugins/domain/user/...`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add backend/plugins/domain/user/
git commit -m "refactor(user): decouple repository from direct database infra import"
```
---
### Task 4: Full Suite Verification & Quality Gate
**Files:**
- Entire repository
- [ ] **Step 1: Run all backend tests**
Run: `cd backend && go test -v ./...`
Expected: ALL PASS
- [ ] **Step 2: Run code-check and format**
Run: `make code-check && make format`
Expected: 0 lint errors, clean formatting.
- [ ] **Step 3: Commit any formatting or lint fixes**
```bash
git commit -am "chore: format and verify code quality"
```