|
|
@@ -0,0 +1,659 @@
|
|
|
+# DDD Naming Governance First Loop 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:** Establish the first DDD governance loop by moving report generation lifecycle status into `domain`, mapping it back to public contract responses, and adding an architecture-test baseline that prevents new domain dependency violations.
|
|
|
+
|
|
|
+**Architecture:** Keep the public API stable while shifting ownership of lifecycle language from `contracts` to `domain`. `domain` owns `ReportGenerationStatus`; `application` maps domain status to `contracts.ExamSprintReportGenerationStatus`; `runtime` continues returning the same JSON values. Architecture tests live in `ability-center-runtime` because that module sees the complete assembled classpath.
|
|
|
+
|
|
|
+**Tech Stack:** Java 17, Maven multi-module build, Spring Boot 3.3.5, JUnit 5, ArchUnit, existing Spring Boot test setup.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+> **Execution note:** Do not create git commits unless the user explicitly asks for commits. Treat each “commit checkpoint” as a local verification checkpoint until commit permission is given.
|
|
|
+
|
|
|
+## Target File Structure
|
|
|
+
|
|
|
+### Create
|
|
|
+
|
|
|
+- `abilities/exam-sprint/domain/src/main/java/cn/yunzhixue/ability/center/examsprint/domain/report/ReportGenerationStatus.java`
|
|
|
+ - Domain-owned lifecycle status for report generation.
|
|
|
+- `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportContractMapper.java`
|
|
|
+ - Package-private application mapper from domain concepts to public contract types.
|
|
|
+- `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportContractMapperTest.java`
|
|
|
+ - Verifies every domain status maps to the existing public contract enum value.
|
|
|
+- `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/architecture/ExamSprintArchitectureTest.java`
|
|
|
+ - Baseline architecture tests with current debt allowlisted.
|
|
|
+
|
|
|
+### Modify
|
|
|
+
|
|
|
+- `abilities/exam-sprint/domain/src/main/java/cn/yunzhixue/ability/center/examsprint/domain/report/ExamSprintReport.java`
|
|
|
+ - Use domain `ReportGenerationStatus` instead of contract `ExamSprintReportGenerationStatus`.
|
|
|
+- `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/DefaultExamSprintReportApplicationService.java`
|
|
|
+ - Compare against domain `ReportGenerationStatus` and map response status back to contract status.
|
|
|
+- `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportGenerationPipeline.java`
|
|
|
+ - Compare against domain `ReportGenerationStatus`.
|
|
|
+- `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportApplicationServiceTest.java`
|
|
|
+ - Update domain status assertions where tests inspect `ExamSprintReport`; keep response assertions on contract status.
|
|
|
+- `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportGenerationWorkerTest.java`
|
|
|
+ - Update domain status assertions.
|
|
|
+- `ability-center-runtime/pom.xml`
|
|
|
+ - Add ArchUnit test dependency.
|
|
|
+
|
|
|
+## Task 1: Add domain-owned generation status
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Create: `abilities/exam-sprint/domain/src/main/java/cn/yunzhixue/ability/center/examsprint/domain/report/ReportGenerationStatus.java`
|
|
|
+- Modify: `abilities/exam-sprint/domain/src/main/java/cn/yunzhixue/ability/center/examsprint/domain/report/ExamSprintReport.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Run the current domain tests as a baseline**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl abilities/exam-sprint/domain -am test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS in a clean workspace. If it fails before editing, stop and record the exact failing module/test before continuing so the governance migration is not blamed for a pre-existing failure.
|
|
|
+
|
|
|
+- [ ] **Step 2: Create `ReportGenerationStatus` in domain**
|
|
|
+
|
|
|
+Create `abilities/exam-sprint/domain/src/main/java/cn/yunzhixue/ability/center/examsprint/domain/report/ReportGenerationStatus.java`:
|
|
|
+
|
|
|
+```java
|
|
|
+package cn.yunzhixue.ability.center.examsprint.domain.report;
|
|
|
+
|
|
|
+public enum ReportGenerationStatus {
|
|
|
+ PENDING,
|
|
|
+ PROCESSING,
|
|
|
+ SUCCESS,
|
|
|
+ FAILED,
|
|
|
+ EXPIRED
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Update `ExamSprintReport` to use the domain status**
|
|
|
+
|
|
|
+In `ExamSprintReport.java`, remove this import:
|
|
|
+
|
|
|
+```java
|
|
|
+import cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportGenerationStatus;
|
|
|
+```
|
|
|
+
|
|
|
+Change the record field from:
|
|
|
+
|
|
|
+```java
|
|
|
+ExamSprintReportGenerationStatus generationStatus,
|
|
|
+```
|
|
|
+
|
|
|
+to:
|
|
|
+
|
|
|
+```java
|
|
|
+ReportGenerationStatus generationStatus,
|
|
|
+```
|
|
|
+
|
|
|
+Change all status references in the same file:
|
|
|
+
|
|
|
+```java
|
|
|
+ExamSprintReportGenerationStatus.PENDING
|
|
|
+ExamSprintReportGenerationStatus.PROCESSING
|
|
|
+ExamSprintReportGenerationStatus.SUCCESS
|
|
|
+ExamSprintReportGenerationStatus.FAILED
|
|
|
+ExamSprintReportGenerationStatus.EXPIRED
|
|
|
+```
|
|
|
+
|
|
|
+to:
|
|
|
+
|
|
|
+```java
|
|
|
+ReportGenerationStatus.PENDING
|
|
|
+ReportGenerationStatus.PROCESSING
|
|
|
+ReportGenerationStatus.SUCCESS
|
|
|
+ReportGenerationStatus.FAILED
|
|
|
+ReportGenerationStatus.EXPIRED
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: Run domain tests to verify the domain module compiles**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl abilities/exam-sprint/domain -am test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: domain module compiles. Remaining domain-to-contract dependency still exists through `ExamSprintReportType`, `ExamSprintReportRenderer`, and `ExamSprintReportStorage`; this task only migrates generation status.
|
|
|
+
|
|
|
+- [ ] **Commit checkpoint, only if explicitly requested**
|
|
|
+
|
|
|
+Suggested message:
|
|
|
+
|
|
|
+```text
|
|
|
+refactor: move report generation status into domain
|
|
|
+```
|
|
|
+
|
|
|
+## Task 2: Add application contract mapping
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Create: `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportContractMapper.java`
|
|
|
+- Create: `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportContractMapperTest.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Write the mapper test first**
|
|
|
+
|
|
|
+Create `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportContractMapperTest.java`:
|
|
|
+
|
|
|
+```java
|
|
|
+package cn.yunzhixue.ability.center.examsprint.application.report;
|
|
|
+
|
|
|
+import cn.yunzhixue.ability.center.examsprint.domain.report.ReportGenerationStatus;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+
|
|
|
+import static org.assertj.core.api.Assertions.assertThat;
|
|
|
+
|
|
|
+class ExamSprintReportContractMapperTest {
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void mapsEveryDomainGenerationStatusToContractStatusWithTheSamePublicName() {
|
|
|
+ for (ReportGenerationStatus domainStatus : ReportGenerationStatus.values()) {
|
|
|
+ assertThat(ExamSprintReportContractMapper.toContractStatus(domainStatus).name())
|
|
|
+ .isEqualTo(domainStatus.name());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void mapsNullGenerationStatusToNull() {
|
|
|
+ assertThat(ExamSprintReportContractMapper.toContractStatus(null)).isNull();
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Run the mapper test and verify it fails before implementation**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl abilities/exam-sprint/application -Dtest=ExamSprintReportContractMapperTest test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: FAIL because `ExamSprintReportContractMapper` does not exist.
|
|
|
+
|
|
|
+- [ ] **Step 3: Implement the mapper**
|
|
|
+
|
|
|
+Create `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportContractMapper.java`:
|
|
|
+
|
|
|
+```java
|
|
|
+package cn.yunzhixue.ability.center.examsprint.application.report;
|
|
|
+
|
|
|
+import cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportGenerationStatus;
|
|
|
+import cn.yunzhixue.ability.center.examsprint.domain.report.ReportGenerationStatus;
|
|
|
+
|
|
|
+final class ExamSprintReportContractMapper {
|
|
|
+
|
|
|
+ private ExamSprintReportContractMapper() {
|
|
|
+ }
|
|
|
+
|
|
|
+ static ExamSprintReportGenerationStatus toContractStatus(ReportGenerationStatus status) {
|
|
|
+ return status == null ? null : ExamSprintReportGenerationStatus.valueOf(status.name());
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: Run the mapper test again**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl abilities/exam-sprint/application -Dtest=ExamSprintReportContractMapperTest test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS.
|
|
|
+
|
|
|
+- [ ] **Commit checkpoint, only if explicitly requested**
|
|
|
+
|
|
|
+Suggested message:
|
|
|
+
|
|
|
+```text
|
|
|
+refactor: map domain generation status to contracts
|
|
|
+```
|
|
|
+
|
|
|
+## Task 3: Update application code to use domain status internally
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/DefaultExamSprintReportApplicationService.java`
|
|
|
+- Modify: `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportGenerationPipeline.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Run application tests before editing**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl abilities/exam-sprint/application -am test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: FAIL after Task 1 if application still imports contract `ExamSprintReportGenerationStatus` for domain status comparisons.
|
|
|
+
|
|
|
+- [ ] **Step 2: Update imports in `DefaultExamSprintReportApplicationService`**
|
|
|
+
|
|
|
+Replace:
|
|
|
+
|
|
|
+```java
|
|
|
+import cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportGenerationStatus;
|
|
|
+```
|
|
|
+
|
|
|
+with:
|
|
|
+
|
|
|
+```java
|
|
|
+import cn.yunzhixue.ability.center.examsprint.domain.report.ReportGenerationStatus;
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Update status comparisons in `DefaultExamSprintReportApplicationService`**
|
|
|
+
|
|
|
+Replace comparisons such as:
|
|
|
+
|
|
|
+```java
|
|
|
+generatedReport.generationStatus() != ExamSprintReportGenerationStatus.SUCCESS
|
|
|
+report.generationStatus() != ExamSprintReportGenerationStatus.EXPIRED
|
|
|
+report.generationStatus() == ExamSprintReportGenerationStatus.SUCCESS
|
|
|
+```
|
|
|
+
|
|
|
+with:
|
|
|
+
|
|
|
+```java
|
|
|
+generatedReport.generationStatus() != ReportGenerationStatus.SUCCESS
|
|
|
+report.generationStatus() != ReportGenerationStatus.EXPIRED
|
|
|
+report.generationStatus() == ReportGenerationStatus.SUCCESS
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: Map status when constructing contract responses**
|
|
|
+
|
|
|
+In `DefaultExamSprintReportApplicationService`, every `new CreateExamSprintReportResponse`, `new CreateExamSprintReportWithUrlResponse`, and `new ExamSprintReportDetailResponse` call must pass:
|
|
|
+
|
|
|
+```java
|
|
|
+ExamSprintReportContractMapper.toContractStatus(report.generationStatus())
|
|
|
+```
|
|
|
+
|
|
|
+or the equivalent variable-specific expression instead of passing `report.generationStatus()` directly.
|
|
|
+
|
|
|
+For example, change:
|
|
|
+
|
|
|
+```java
|
|
|
+return new CreateExamSprintReportResponse(
|
|
|
+ report.reportId(),
|
|
|
+ report.reportType(),
|
|
|
+ report.generationStatus(),
|
|
|
+ report.createdAt(),
|
|
|
+ report.expiresAt());
|
|
|
+```
|
|
|
+
|
|
|
+to:
|
|
|
+
|
|
|
+```java
|
|
|
+return new CreateExamSprintReportResponse(
|
|
|
+ report.reportId(),
|
|
|
+ report.reportType(),
|
|
|
+ ExamSprintReportContractMapper.toContractStatus(report.generationStatus()),
|
|
|
+ report.createdAt(),
|
|
|
+ report.expiresAt());
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 5: Update imports and comparisons in `ExamSprintReportGenerationPipeline`**
|
|
|
+
|
|
|
+Replace:
|
|
|
+
|
|
|
+```java
|
|
|
+import cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportGenerationStatus;
|
|
|
+```
|
|
|
+
|
|
|
+with:
|
|
|
+
|
|
|
+```java
|
|
|
+import cn.yunzhixue.ability.center.examsprint.domain.report.ReportGenerationStatus;
|
|
|
+```
|
|
|
+
|
|
|
+Replace:
|
|
|
+
|
|
|
+```java
|
|
|
+report.generationStatus() != ExamSprintReportGenerationStatus.PENDING
|
|
|
+```
|
|
|
+
|
|
|
+with:
|
|
|
+
|
|
|
+```java
|
|
|
+report.generationStatus() != ReportGenerationStatus.PENDING
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 6: Compile the application module**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl abilities/exam-sprint/application -am -DskipTests compile
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS. Full application tests run in Task 4 after test imports distinguish domain status from contract response status.
|
|
|
+
|
|
|
+- [ ] **Commit checkpoint, only if explicitly requested**
|
|
|
+
|
|
|
+Suggested message:
|
|
|
+
|
|
|
+```text
|
|
|
+refactor: use domain generation status in application flow
|
|
|
+```
|
|
|
+
|
|
|
+## Task 4: Update tests for domain-vs-contract status ownership
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportApplicationServiceTest.java`
|
|
|
+- Modify: `abilities/exam-sprint/application/src/test/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportGenerationWorkerTest.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Locate contract status usages in application tests**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+rg "ExamSprintReportGenerationStatus" "abilities/exam-sprint/application/src/test/java"
|
|
|
+```
|
|
|
+
|
|
|
+Expected: matches in application tests that currently import the contract enum.
|
|
|
+
|
|
|
+- [ ] **Step 2: Split status imports by assertion target**
|
|
|
+
|
|
|
+Use this rule:
|
|
|
+
|
|
|
+- assertions against `ExamSprintReport.generationStatus()` use `cn.yunzhixue.ability.center.examsprint.domain.report.ReportGenerationStatus`;
|
|
|
+- assertions against `CreateExamSprintReportResponse`, `CreateExamSprintReportWithUrlResponse`, or `ExamSprintReportDetailResponse` use `cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportGenerationStatus`.
|
|
|
+
|
|
|
+When both are needed in the same test file, import the domain enum and refer to the contract enum with a fully qualified name to avoid ambiguous names.
|
|
|
+
|
|
|
+Example domain assertion:
|
|
|
+
|
|
|
+```java
|
|
|
+assertThat(savedReport.generationStatus()).isEqualTo(ReportGenerationStatus.SUCCESS);
|
|
|
+```
|
|
|
+
|
|
|
+Example contract response assertion:
|
|
|
+
|
|
|
+```java
|
|
|
+assertThat(response.generationStatus())
|
|
|
+ .isEqualTo(cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportGenerationStatus.SUCCESS);
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Run application tests**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl abilities/exam-sprint/application -am test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS for application module tests.
|
|
|
+
|
|
|
+- [ ] **Step 4: Run runtime tests to verify response compatibility**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl ability-center-runtime -am test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: runtime tests pass and API responses still expose the original public enum names: `PENDING`, `PROCESSING`, `SUCCESS`, `FAILED`, `EXPIRED`.
|
|
|
+
|
|
|
+- [ ] **Commit checkpoint, only if explicitly requested**
|
|
|
+
|
|
|
+Suggested message:
|
|
|
+
|
|
|
+```text
|
|
|
+test: distinguish domain and contract generation status
|
|
|
+```
|
|
|
+
|
|
|
+## Task 5: Add architecture baseline tests
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `ability-center-runtime/pom.xml`
|
|
|
+- Create: `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/architecture/ExamSprintArchitectureTest.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add ArchUnit dependency to runtime test scope**
|
|
|
+
|
|
|
+In `ability-center-runtime/pom.xml`, add this dependency inside `<dependencies>`:
|
|
|
+
|
|
|
+```xml
|
|
|
+<dependency>
|
|
|
+ <groupId>com.tngtech.archunit</groupId>
|
|
|
+ <artifactId>archunit-junit5</artifactId>
|
|
|
+ <version>1.3.0</version>
|
|
|
+ <scope>test</scope>
|
|
|
+</dependency>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Write architecture tests with current domain debt allowlisted**
|
|
|
+
|
|
|
+Create `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/architecture/ExamSprintArchitectureTest.java`:
|
|
|
+
|
|
|
+```java
|
|
|
+package cn.yunzhixue.ability.center.architecture;
|
|
|
+
|
|
|
+import com.tngtech.archunit.core.domain.JavaClass;
|
|
|
+import com.tngtech.archunit.core.importer.ImportOption;
|
|
|
+import com.tngtech.archunit.junit.AnalyzeClasses;
|
|
|
+import com.tngtech.archunit.junit.ArchTest;
|
|
|
+import com.tngtech.archunit.lang.ArchRule;
|
|
|
+import com.tngtech.archunit.base.DescribedPredicate;
|
|
|
+
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
|
|
|
+
|
|
|
+@AnalyzeClasses(
|
|
|
+ packages = "cn.yunzhixue.ability.center.examsprint",
|
|
|
+ importOptions = ImportOption.DoNotIncludeTests.class)
|
|
|
+class ExamSprintArchitectureTest {
|
|
|
+
|
|
|
+ private static final Set<String> CURRENT_DOMAIN_CONTRACT_DEBT = Set.of(
|
|
|
+ "ExamSprintReport",
|
|
|
+ "ExamSprintReportRenderer",
|
|
|
+ "ExamSprintReportStorage");
|
|
|
+
|
|
|
+ private static final Set<String> CURRENT_DOMAIN_JACKSON_DEBT = Set.of(
|
|
|
+ "ExamSprintReport",
|
|
|
+ "ExamSprintReportRenderer");
|
|
|
+
|
|
|
+ @ArchTest
|
|
|
+ static final ArchRule contracts_should_not_depend_on_inner_layers = noClasses()
|
|
|
+ .that().resideInAPackage("..contracts..")
|
|
|
+ .should().dependOnClassesThat().resideInAnyPackage(
|
|
|
+ "..domain..",
|
|
|
+ "..application..",
|
|
|
+ "..infrastructure..",
|
|
|
+ "..adapter.."
|
|
|
+ );
|
|
|
+
|
|
|
+ @ArchTest
|
|
|
+ static final ArchRule infrastructure_should_not_depend_on_runtime_adapters = noClasses()
|
|
|
+ .that().resideInAPackage("..infrastructure..")
|
|
|
+ .should().dependOnClassesThat().resideInAnyPackage(
|
|
|
+ "..adapter..",
|
|
|
+ "..configuration.."
|
|
|
+ );
|
|
|
+
|
|
|
+ @ArchTest
|
|
|
+ static final ArchRule new_domain_classes_should_not_depend_on_contracts = noClasses()
|
|
|
+ .that().resideInAPackage("..domain..")
|
|
|
+ .and(areNotNamed(CURRENT_DOMAIN_CONTRACT_DEBT))
|
|
|
+ .should().dependOnClassesThat().resideInAPackage("..contracts..");
|
|
|
+
|
|
|
+ @ArchTest
|
|
|
+ static final ArchRule new_domain_classes_should_not_depend_on_jackson = noClasses()
|
|
|
+ .that().resideInAPackage("..domain..")
|
|
|
+ .and(areNotNamed(CURRENT_DOMAIN_JACKSON_DEBT))
|
|
|
+ .should().dependOnClassesThat().resideInAnyPackage(
|
|
|
+ "com.fasterxml.jackson.."
|
|
|
+ );
|
|
|
+
|
|
|
+ private static DescribedPredicate<JavaClass> areNotNamed(Set<String> simpleNames) {
|
|
|
+ return new DescribedPredicate<>("are not named " + simpleNames) {
|
|
|
+ @Override
|
|
|
+ public boolean test(JavaClass input) {
|
|
|
+ return !simpleNames.contains(input.getSimpleName());
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Run architecture tests**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl ability-center-runtime -Dtest=ExamSprintArchitectureTest test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS. If this fails because additional historical debt exists, add only the exact existing class names to the relevant `CURRENT_DOMAIN_*_DEBT` set and document why in the PR description.
|
|
|
+
|
|
|
+- [ ] **Step 4: Run all runtime tests**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q -pl ability-center-runtime -am test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS.
|
|
|
+
|
|
|
+- [ ] **Commit checkpoint, only if explicitly requested**
|
|
|
+
|
|
|
+Suggested message:
|
|
|
+
|
|
|
+```text
|
|
|
+test: add exam sprint architecture guardrails
|
|
|
+```
|
|
|
+
|
|
|
+## Task 6: Remove the migrated contract status dependency from domain imports
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Verify: `abilities/exam-sprint/domain/src/main/java/cn/yunzhixue/ability/center/examsprint/domain/report/ExamSprintReport.java`
|
|
|
+- Verify: `abilities/exam-sprint/domain/src/main/java/cn/yunzhixue/ability/center/examsprint/domain/report/*.java`
|
|
|
+- Verify: `abilities/exam-sprint/domain/pom.xml`
|
|
|
+
|
|
|
+- [ ] **Step 1: Search for remaining contract status imports in domain**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+rg "ExamSprintReportGenerationStatus" "abilities/exam-sprint/domain/src/main/java"
|
|
|
+```
|
|
|
+
|
|
|
+Expected: no matches.
|
|
|
+
|
|
|
+- [ ] **Step 2: Search for remaining domain contract imports**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+rg "contracts\.report" "abilities/exam-sprint/domain/src/main/java"
|
|
|
+```
|
|
|
+
|
|
|
+Expected: matches remain only for the current known debt around `ExamSprintReportType`, likely in:
|
|
|
+
|
|
|
+```text
|
|
|
+ExamSprintReport.java
|
|
|
+ExamSprintReportRenderer.java
|
|
|
+ExamSprintReportStorage.java
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Leave `domain/pom.xml` contracts dependency in place for this loop**
|
|
|
+
|
|
|
+Do not remove this dependency yet:
|
|
|
+
|
|
|
+```xml
|
|
|
+<dependency>
|
|
|
+ <groupId>cn.yunzhixue</groupId>
|
|
|
+ <artifactId>exam-sprint-contracts</artifactId>
|
|
|
+ <version>${project.version}</version>
|
|
|
+</dependency>
|
|
|
+```
|
|
|
+
|
|
|
+Reason: `ExamSprintReportType` still lives in contracts. Removing the dependency belongs to the next governance loop that migrates `ReportType`.
|
|
|
+
|
|
|
+- [ ] **Step 4: Run full reactor tests**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS. If unrelated failures occur, record the exact failing module and test names before deciding whether to fix them in this loop or defer.
|
|
|
+
|
|
|
+- [ ] **Commit checkpoint, only if explicitly requested**
|
|
|
+
|
|
|
+Suggested message:
|
|
|
+
|
|
|
+```text
|
|
|
+refactor: reduce domain dependency on report contracts
|
|
|
+```
|
|
|
+
|
|
|
+## Task 7: Update governance tracking after implementation
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `docs/superpowers/specs/2026-04-27-ddd-naming-governance-design.md`
|
|
|
+- Modify: `docs/superpowers/plans/2026-04-27-ddd-naming-governance-first-loop.md` only if execution notes differ from this plan.
|
|
|
+
|
|
|
+- [ ] **Step 1: Update the debt register result**
|
|
|
+
|
|
|
+In `docs/superpowers/specs/2026-04-27-ddd-naming-governance-design.md`, update the debt register entry for `domain -> contracts` to mention that `ReportGenerationStatus` has been migrated and `ReportType` remains.
|
|
|
+
|
|
|
+Use wording like:
|
|
|
+
|
|
|
+```markdown
|
|
|
+| `domain -> contracts` | `ReportType` still reuses API enum after `ReportGenerationStatus` migration | domain-owned `ReportType` plus application mapper |
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Add next-loop recommendation**
|
|
|
+
|
|
|
+In the same design document, under “First Governance Loop”, add the next recommended loop:
|
|
|
+
|
|
|
+```markdown
|
|
|
+Next recommended loop: migrate `ReportType` into domain, remove `exam-sprint-domain` dependency on `exam-sprint-contracts`, and tighten the architecture allowlist accordingly.
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Run documentation grep checks**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+rg "ReportGenerationStatus" "docs/superpowers/specs/2026-04-27-ddd-naming-governance-design.md" "docs/superpowers/plans/2026-04-27-ddd-naming-governance-first-loop.md"
|
|
|
+```
|
|
|
+
|
|
|
+Expected: matches describe both the completed first loop and next governance direction clearly.
|
|
|
+
|
|
|
+- [ ] **Step 4: Final verification**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -q test
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS.
|
|
|
+
|
|
|
+- [ ] **Commit checkpoint, only if explicitly requested**
|
|
|
+
|
|
|
+Suggested message:
|
|
|
+
|
|
|
+```text
|
|
|
+docs: record ddd governance first loop outcome
|
|
|
+```
|
|
|
+
|
|
|
+## Implementation Handoff
|
|
|
+
|
|
|
+Recommended execution mode:
|
|
|
+
|
|
|
+1. Use subagent-driven development, one task per subagent, because tasks are mostly independent after Task 1 establishes the enum.
|
|
|
+2. Review after each task with emphasis on API compatibility and dependency direction.
|
|
|
+3. Run `mvn -q test` before claiming the loop is complete.
|
|
|
+
|
|
|
+Do not proceed to the second governance loop until:
|
|
|
+
|
|
|
+- full tests have passed;
|
|
|
+- architecture tests are passing;
|
|
|
+- external response enum values remain unchanged;
|
|
|
+- remaining `domain -> contracts` debt is documented as `ReportType` migration work.
|