Some checks failed
Build and publish Docker image / Build and push (push) Failing after 1m27s
Java 21 / Spring Boot 3.5.3 multi-module Maven project. Hybrid BM25+HNSW search with RRF, cross-encoder reranker, ONNX Runtime 1.22.0 (CPU + CUDA 12 GPU variants).
92 lines
5.9 KiB
Markdown
92 lines
5.9 KiB
Markdown
# trueref — Code Style
|
|
|
|
## 1. Language & Toolchain
|
|
- **Java 21**, source/target 21.
|
|
- **Maven** with `spring-boot-maven-plugin` for the fat JAR.
|
|
- **Spotless** with **Palantir Java Format** for formatting (4-space indent, 120 col).
|
|
- **ErrorProne** + **NullAway** for static analysis. NullAway annotations: `@org.jspecify.annotations.Nullable` / `@NonNull`.
|
|
- Hexagonal boundaries are enforced by **Maven module dependencies** (no ArchUnit). See ARCHITECTURE §3.
|
|
|
|
## 2. Records, Sealed Types, Pattern Matching
|
|
- Prefer **records** for DTOs, value objects, and `port.in`/`port.out` parameter/result types.
|
|
- Use **sealed interfaces** for closed result/event hierarchies (`sealed interface IngestionEvent permits ...`).
|
|
- Use **pattern matching** (`switch` expressions, `instanceof`) over visitor pattern.
|
|
|
|
## 3. Nullability & Optional
|
|
- All API surfaces (public methods on ports, REST DTOs) are **non-null by default**; mark nullable explicitly with `@Nullable`.
|
|
- Use `Optional<T>` **only** as a return type from query-style methods. Never as a field, never as a parameter.
|
|
|
|
## 4. Concurrency
|
|
- Spawn virtual threads via `Thread.ofVirtual().start(...)` or `Executors.newVirtualThreadPerTaskExecutor()`. **Never** call `Thread.sleep` inside a synchronized block.
|
|
- Shared mutable state is forbidden in `domain` and discouraged in `application`. When unavoidable, use `java.util.concurrent.atomic.*` or a `ReentrantLock`.
|
|
- GPU work goes through `GpuSemaphore.acquire()` (a thin wrapper around `Semaphore`).
|
|
- Long-running orchestration uses **structured concurrency** (`StructuredTaskScope`) where it improves cancellation safety.
|
|
|
|
## 5. Error Handling
|
|
- **Domain errors** are sealed exception hierarchies rooted at `TrueRefException`. Adapters translate them to HTTP/JSON-RPC errors centrally (REST: `@ControllerAdvice`; MCP: dedicated translator).
|
|
- **No checked exceptions** at port boundaries. Wrap third-party checked exceptions at the adapter edge.
|
|
- Validation errors carry a stable `code` (string) so the UI can localize.
|
|
- Never `catch (Exception e)` and swallow. Either log + rethrow as a domain exception or let it propagate.
|
|
|
|
## 6. Logging
|
|
- **SLF4J** with parameterized messages: `log.info("indexed tag {} of repo {}", tag, repoName);` — never string-concatenate.
|
|
- Structured fields via MDC: `repoId`, `versionId`, `jobId`, `stage`. Cleared in a try/finally.
|
|
- Log levels:
|
|
- `ERROR`: unrecoverable, requires operator attention.
|
|
- `WARN`: degraded, automatic recovery in progress.
|
|
- `INFO`: lifecycle events (job started/finished, repo registered).
|
|
- `DEBUG`: per-file, per-chunk detail. Off by default.
|
|
|
|
## 7. Naming
|
|
- Use cases (`port.in`): imperative verb phrases — `IndexVersion`, `ResolveLibraryId`.
|
|
- SPIs (`port.out`): noun-ish role names — `EmbeddingService`, `ChunkStore`, `GitClient`.
|
|
- Adapter classes: `<Tech><Role>` — `LuceneChunkStore`, `OnnxEmbeddingService`, `JGitClient`.
|
|
- DTOs: `<Resource><Action>Request` / `<Resource>Response`.
|
|
- Records' field names are camelCase (no Hungarian, no `_` prefixes).
|
|
|
|
## 8. Package & File Discipline
|
|
- One public type per file.
|
|
- Internal helpers are package-private. Avoid `public` unless used across packages.
|
|
- Domain packages export **only** records and interfaces. No Spring annotations, no Lombok, no Jackson annotations.
|
|
- Adapter packages may use Spring stereotypes (`@Component`, `@Repository`, `@RestController`) but adapters depend on **port interfaces only** when interacting with the application.
|
|
|
|
## 9. Spring Wiring
|
|
- Wiring lives in `bootstrap`. Each adapter package may define a `@Configuration` (constructor-injected `@Bean` factories) but **does not** auto-`@ComponentScan` itself; bootstrap explicitly imports.
|
|
- Use `@ConfigurationProperties` records for typed config; never raw `@Value`.
|
|
- Prefer constructor injection. **No field injection.**
|
|
|
|
## 10. Persistence (H2)
|
|
- Migrations under `src/main/resources/db/migration` named `V<N>__<snake_case>.sql`. Flyway runs at startup.
|
|
- All access via Spring **`JdbcClient`** (Spring Boot 3.2+, fluent JDBC). **No JPA/Hibernate**, no `JdbcTemplate` directly.
|
|
- Mappers are explicit `RowMapper<T>` lambdas, not reflection-based.
|
|
- SQL lives next to the repository class, either as `static final String` constants or in `*.sql` files loaded via `ClassPathResource` for non-trivial queries.
|
|
|
|
## 11. REST
|
|
- Controllers in `adapter.in.rest`. They depend only on `port.in` interfaces and DTO records.
|
|
- DTOs are **separate** from domain records. Mapping via plain `static of(...)` factories. **No MapStruct.**
|
|
- All endpoints documented with `@Operation`, `@ApiResponses`, `@Schema` (springdoc).
|
|
- Request validation via `jakarta.validation` annotations on DTOs.
|
|
- SSE endpoints return `SseEmitter`; subscribe to `JobEventBus`, unsubscribe on completion/timeout.
|
|
|
|
## 12. MCP
|
|
- Tool definitions are **records** decorated to produce JSON Schema via Spring AI's MCP support. Schema strings stay verbatim (1:1 with Context7) so LLMs see identical contracts.
|
|
- Tool handlers depend only on `port.in` (`SearchLibraryDocs`, `ResolveLibraryId`).
|
|
|
|
## 13. Tests
|
|
- **JUnit 5** + **AssertJ** + **Mockito** (sparingly).
|
|
- Unit tests live next to the package they test. Integration tests under `src/test/java/.../it/` and use `@SpringBootTest`.
|
|
- Use **Testcontainers** only when truly required (we mostly avoid it via embedded stores).
|
|
- ArchUnit test suite is mandatory and runs in CI.
|
|
|
|
## 14. Dependency Hygiene
|
|
- BOM-managed versions only. Add a dependency only if it provides clear value over JDK + Spring Boot + already-included libs.
|
|
- No Lombok, no Guava (use JDK 21 equivalents), no Reactor (we use virtual threads + blocking).
|
|
- No Kotlin, no Scala.
|
|
|
|
## 15. Documentation
|
|
- All architectural decisions go in **ARCHITECTURE.md**.
|
|
- All research notes go in **FINDINGS.md** with sources.
|
|
- All conventions go in **this file**.
|
|
- Per-package `package-info.java` may exist for non-trivial packages, summarizing role and exported types.
|
|
- No README sprawl: `README.md` is a quickstart only and links to the three docs above.
|