# Premium DisplayEvent Refactoring Plan

## Goal

Emit correctly attributed DisplayEvents for Premium DM, Import Into, CDC, and
Private Endpoint operations. Preserve existing state persistence, concurrency,
and retry semantics; CAS changes are explicitly outside this PR. Kafka delivery
remains best-effort.

This plan tracks PR #3540.

## Analysis

### Transition contract

| Domain | Persistence scope | Event rule |
|---|---|---|
| Premium DM | Preserve existing stage-filtered updates | Publish after the existing successful update path |
| Import Into | Preserve each existing worker persistence path | Publish after a visible status change is persisted |
| Private Endpoint | Preserve existing model/repository behavior | Publish after the existing transaction or save succeeds |
| CDC | Preserve existing event hook | Add Private Link endpoint attribution |

### Event delivery contract

| Surface | `display_event_type` | Detail ID | Visible lifecycle |
|---|---|---|---|
| Premium DM | `DataMigration` | `MigrationId` string | started, running, paused, resumed, failed, deleting, deleted |
| Import Into | `ImportData` | `ImportId` uint64 | started, running, succeeded, failed, canceled |
| Private Endpoint | `CreatePrivateEndpointForExternalServices` / `DeletePrivateEndpointForExternalServices` | `EndpointId` uint64 | started and terminal result |
| CDC | Existing changefeed event types | `ChangefeedID` uint64 | existing lifecycle |

The EventCenter publisher is constructor-injected. New code must not read a
global publisher singleton. Pure event builders remain independently testable.

### Symbols to keep

- Existing event type strings and CDC dual-emission behavior.
- `BuildEventBasicInfos` for numeric context IDs.
- A string context-ID builder for Premium DM task IDs.
- Best-effort asynchronous EventCenter publishing.
- Existing domain state machines and retry behavior.

### Symbols to remove or replace

- Remove `GetEventCenterPublisher` and the global publisher variable.
- Remove the per-attempt Premium DM delete-failed DisplayEvent. Retryable
  deletion failures remain logs/metrics, not Activity Feed terminal events.
- Do not introduce expected-stage/status updates, optimistic-locking behavior,
  or new idempotency semantics in this PR.

## Execution Steps

1. Refactor EventCenter dependency ownership.
   - Make `NewEventCenterPublisher` return an instance without mutating global
     state.
   - Inject the publisher through container constructors into affected
     controllers, models, and workers.
   - Success: no new event publisher calls use a global getter; mocked
     publishers can verify trigger chains.

2. Add Import Into events without changing persistence.
   - Add event conversion and publisher injection.
   - Emit from create and the existing Preparing, Scaling, Importing,
     Canceling, and cluster-deleted persistence paths.
   - Success: lifecycle events have correct attribution and payloads while the
     underlying updates, cleanup, and error handling match the base branch.

3. Add Premium DM events without changing transitions.
   - Keep the existing `UpdateTask` DAO and stage filters.
   - Publish lifecycle events after existing successful controller/worker
     paths.
   - Do not publish retryable deletion-attempt failures.
   - Success: deleting to deleted emits a successful event even with a
     historical task error, with no CAS behavior added.

4. Complete Private Endpoint attribution.
   - Require an authenticated actor for create/delete controller paths.
   - Persist stable operation attribution for asynchronous terminal events.
   - Publish start and terminal events only after successful saves.
   - Success: missing actors are rejected and worker events use the initiating
     operation's actor and cluster context.

5. Regenerate and verify.
   - Regenerate entity query-set code for schema additions.
   - Run formatting checks without leaving generated drift.
   - Use `.vscode/settings.json` GOPATH/GOROOT/GOMODCACHE for all Go commands.

6. Keep Premium DM deletion maintainable.
   - Split `OnTaskDeleting` into orchestration and focused cleanup helpers
     without changing cleanup order, retryability, or best-effort operations.
   - Success: `maintidx` accepts `OnTaskDeleting`, deletion tests pass, and the
     NextGen Go lint target exits 0.

```bash
cd dataflow-service-ng
go test ./app/models/import_into/... ./app/controllers/import_into/... -count=1
go test ./app/models/premium_dm/... ./app/controllers/premium_dm/... -count=1
go test ./app/models/sink_endpoint/... ./app/controllers/console/... -count=1
go test ./app/event_bus/publisher/center/... ./app/models/biz_changefeed/... -count=1
cd ..
git diff --check
make app-unit-test-local
make -C dataflow-service-ng lint-golang
```

Success requires every command to exit 0, no new CAS/state-transition behavior
relative to the base branch, and no event after a failed persistence operation.

## Risk Assessment

| Risk | Mitigation |
|---|---|
| Constructor changes break wiring or tests | Update container wiring and use injected mocks in trigger-chain tests |
| Existing transition behavior changes accidentally | Diff state persistence paths against the base branch and keep event calls additive |
| Existing rows lack new attribution | Keep explicit zero defaults; new operations populate stable attribution |
| Kafka fails after a committed transition | Preserve documented best-effort delivery; outbox is outside this PR |
| Retryable delete errors disappear from Activity Feed | Keep detailed logs/metrics; only durable business terminal failures are events |
