174 lines
9.8 KiB
Markdown
174 lines
9.8 KiB
Markdown
# Harness-Free Module and Gradle Hygiene Design
|
|
|
|
- **Date:** 2026-07-25
|
|
- **Status:** Approved
|
|
- **Mode:** B reconstruction without `.harness`
|
|
- **Scope:** all 19 Gradle leaves, dependency declarations, test baselines, Mongo scaffolding,
|
|
runtime-composition documentation, and dependency locks
|
|
- **Topology SSOT:** `src/config/architecture/modules.json`
|
|
|
|
## 1. Context
|
|
|
|
The 19-leaf project dependency graph obeys the registered allowed edges, and the three core
|
|
production source sets are free of Spring, persistence, transport, logging, and metrics imports.
|
|
The audit nevertheless found a wider declared graph than the source graph, Spring WebMVC test
|
|
libraries on pure-core test classpaths, Boot 3-era OpenAPI tooling on Spring Boot 4, example-domain
|
|
code in the production Mongo adapter, and direct MDC access in sample application services.
|
|
|
|
This design follows the user-approved Mode B reconstruction. It does not recreate or depend on
|
|
`.harness`; settings and verification continue to consume the JSON registry.
|
|
|
|
## 2. Goals
|
|
|
|
1. Keep the exact 19 leaves and all allowed project edges in the JSON registry.
|
|
2. Remove only dependencies proven unnecessary by source/test inspection plus focused
|
|
compile/test verification.
|
|
3. Give `domain-core`, `application-core`, and `shared-contract` JUnit/AssertJ-only test
|
|
classpaths.
|
|
4. Keep Spring Boot 4.0.0 and replace `springdoc-openapi` 2.x with the Boot 4-compatible 3.0.0
|
|
line.
|
|
5. Remove unused direct Jackson 2 declarations from GraphQL and WebSocket.
|
|
6. Require the Spring configuration processor exactly in leaves whose main source declares
|
|
`@ConfigurationProperties`.
|
|
7. Remove adapter-local `Example*` business concepts from `persistence-mongo`; retain only
|
|
opt-in Mongo infrastructure and typed enablement settings.
|
|
8. Replace sample application-layer MDC reads with an application-owned correlation-context port
|
|
implemented by the inbound web adapter.
|
|
9. Remove tracked jqwik runtime state and ignore future `.jqwik-database` files.
|
|
10. Describe the default bootstrap as the default runtime composition, not as wiring every
|
|
optional leaf.
|
|
11. Regenerate only affected strict dependency locks and finish with the full release gates.
|
|
|
|
## 3. Non-goals
|
|
|
|
- No endpoint, persistence schema, public response, outbox transition, or sample-domain behavior
|
|
change.
|
|
- No version catalog, convention-plugin, `buildSrc`, module rename, or registry schema expansion.
|
|
- No automatic addition of GraphQL, gRPC, WebSocket, Mongo, file server, or object storage to the
|
|
default `app-bootstrap` runtime.
|
|
- No stage, commit, amend, or push.
|
|
|
|
## 4. Approved dependency decisions
|
|
|
|
An allowed registry edge is permission, not an obligation to declare it.
|
|
|
|
| Leaf | Remove after focused proof | Preserve |
|
|
| --- | --- | --- |
|
|
| `application-core` | unused `domain-core` edge | `shared-contract` |
|
|
| `inbound:web` | unused `domain-core` edge | application/shared and transport dependencies |
|
|
| `inbound:graphql` | application/domain edges, direct Jackson 2, unused processor | shared and GraphQL/web test transport |
|
|
| `inbound:grpc` | application/domain edges, unused annotations/direct protobuf declarations | shared, netty, services, configuration processor |
|
|
| `inbound:websocket` | application/shared edges, direct Jackson 2 | domain, WebSocket, configuration processor |
|
|
| `outbound:support` | domain/application/shared edges | autoconfigure and SLF4J API |
|
|
| `outbound:cache-redis` | domain/application, unused Groovy/Spock | shared/support |
|
|
| `outbound:httpclient` | domain/application | shared/support, actual Groovy/Spock tests |
|
|
| `outbound:identifier` | domain, `uuid-creator` | application, actual Groovy/Spock tests |
|
|
| `outbound:messaging` | domain, unused Groovy/Spock | application/shared/support/SLF4J |
|
|
| `outbound:notification` | domain, unused Groovy/Spock | application/shared/support/web/SLF4J |
|
|
| `outbound:persistence-jpa` | domain; explicit Flyway core only if focused compile proves the starter sufficient | application/shared/JPA/PostgreSQL |
|
|
| `outbound:persistence-mongo` | application/shared, `Example*`, example Testcontainers tests | Mongo opt-in infrastructure/settings |
|
|
| `outbound:fileserver` | broad Boot starter | application/shared, autoconfigure, SLF4J |
|
|
| `outbound:objectstorage` | broad Boot starter | application/shared/AWS, autoconfigure, SLF4J, vendor IT |
|
|
|
|
Production composition-root dependencies remain even when bootstrap source does not statically
|
|
import their types: their purpose is runtime assembly. Duplicate test declarations may be removed
|
|
only when the focused test classpath continues to compile and execute.
|
|
|
|
## 5. Pure-core test and verification policy
|
|
|
|
`domain-core`, `application-core`, and `shared-contract` receive only JUnit Jupiter, AssertJ, and
|
|
the JUnit launcher from the root convention. All other leaves keep the existing Spring test
|
|
baseline in this change; family-wide convention plugins are out of scope.
|
|
|
|
The existing application dependency-purity gate remains. A new registry-driven configuration
|
|
processor parity gate applies this Boolean invariant to every leaf and is wired into `check`:
|
|
main source contains one or more exact `@ConfigurationProperties(` occurrences if and only if the
|
|
leaf `build.gradle` contains exactly one Spring configuration-processor declaration. It must ignore
|
|
`@ConfigurationPropertiesScan`; the number of settings classes is not compared with the number of
|
|
processor declarations.
|
|
|
|
## 6. Spring Boot 4 compatibility
|
|
|
|
The web adapter changes
|
|
`org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.6` to `3.0.0`, the first stable
|
|
springdoc line released for Spring Boot 4.0.0. The existing sample tests that boot a real server
|
|
and call `/v3/api-docs` are the behavior gate. Snapshot changes are accepted only if they are a
|
|
deterministic library-version result and retain the public API contract.
|
|
|
|
Springdoc 3 otherwise widens `ApiError.details` from the committed `type: object` to an
|
|
unconstrained OAS 3.1 schema. A web-adapter-owned `OpenApiCustomizer` must restore the object schema
|
|
in the final generated document. Both real-server test applications import that production
|
|
configuration. `shared-contract` remains free of Swagger annotations and dependencies.
|
|
|
|
GraphQL and WebSocket remove direct `com.fasterxml.jackson` declarations because neither source
|
|
set imports them and Spring Boot 4 owns its JSON stack through the relevant starters.
|
|
The web adapter retains the `JsonNullable` value type, but its `0.2.6` artifact also declares
|
|
Jackson 2 transitively while this repository supplies explicit Jackson 3 serializers. Before and
|
|
after dependency insight plus focused present/null/undefined serialization tests determine whether
|
|
that transitive edge can be excluded. Exclusion is applied only if those tests and the real-server
|
|
OpenAPI tests pass; springdoc/Swagger's independently required JSON graph is not removed by
|
|
assumption.
|
|
|
|
## 7. Mongo production boundary
|
|
|
|
Delete the adapter-local `ExampleRecord`, document, mapper, repository, repository adapter, and
|
|
their tests. `MongoPersistenceConfig` remains conditional on
|
|
`ca-skeleton.persistence-mongo.enabled=true` and explicitly imports the Mongo client/data
|
|
auto-configurations without owning a fake business repository.
|
|
|
|
The starter also registers Mongo auto-configuration directly through Boot metadata, independently
|
|
of `MongoPersistenceConfig`. A module-level `AutoConfigurationImportFilter`, registered through
|
|
Boot 4's `META-INF/spring.factories` discovery path, must exclude the Boot 4 sync/reactive client,
|
|
data, repository, health, and metrics Mongo auto-configurations while the enable property is absent
|
|
or false. It must allow them unchanged when the property is true; consumers must not need to set
|
|
`spring.autoconfigure.exclude`.
|
|
|
|
Replacement tests must prove:
|
|
|
|
- an actual `@EnableAutoConfiguration` context in default/false mode creates no Mongo
|
|
infrastructure;
|
|
- properties bind the enable flag;
|
|
- enabled mode can create the infrastructure with a supplied mock `MongoClient`, without a real
|
|
network connection;
|
|
- production source contains no `Example*` type.
|
|
|
|
The Testcontainers dependencies leave this module when the example repository IT is removed.
|
|
|
|
## 8. Correlation context boundary
|
|
|
|
`application-core` owns a framework-free `CorrelationIdPort` whose read result is optional.
|
|
`adapter:inbound:web` implements it from the sanitized request MDC correlation key.
|
|
`CreateWorkLogUseCase` and `PosterEventPublisher` depend only on the port and preserve the current
|
|
fallback to the generated event id when no correlation id exists.
|
|
|
|
Tests first pin present/blank/absent behavior and prove the sample application packages no longer
|
|
import SLF4J/MDC. Diagnostic storage remains an adapter concern.
|
|
|
|
## 9. Runtime composition and generated state
|
|
|
|
`app-bootstrap` keeps its current default runtime modules. Its build description and README must
|
|
state that optional leaves require an explicit registry and composition-root dependency change.
|
|
Optional adapters remain independently buildable and testable.
|
|
|
|
The tracked four-byte `src/sample-portfolio/.jqwik-database` is generated runtime state. Delete it
|
|
and add `.jqwik-database` to `src/.gitignore`; retain jqwik itself because property tests use it.
|
|
|
|
## 10. Verification
|
|
|
|
Run focused compile/tests before and after each dependency group. Regenerate locks only through
|
|
each affected leaf's `:leaf-path:resolveAndLockAll --write-locks` task, then run:
|
|
|
|
```bash
|
|
cd src
|
|
./gradlew check --console=plain
|
|
./gradlew test --console=plain
|
|
./gradlew verifyCleanArchitectureDependencies --console=plain
|
|
./gradlew verifyApplicationCoreDependencyPurity --console=plain
|
|
./gradlew verifyConfigurationPropertiesProcessor --console=plain
|
|
./gradlew verifyDependencyLocks --console=plain
|
|
./gradlew verifyPublicPathSnapshot verifyEnvKeys --console=plain
|
|
```
|
|
|
|
Completion requires fresh review, `git diff --check`, and an LLM Wiki branch note or an explicit
|
|
capture blocker for the mandated exact vault path.
|