All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m20s
CI / OCR Service Tests (pull_request) Successful in 25s
CI / Backend Unit Tests (pull_request) Successful in 3m48s
CI / fail2ban Regex (pull_request) Successful in 47s
CI / Semgrep Security Scan (pull_request) Successful in 22s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m7s
Fixes GLOSSARY position-step value (1000→10), adds DEFERRABLE constraint note, and documents GeschichteView, JourneyItemView, and DocumentSummary read-model types. ADR-035 records the decision to use Optional<String> for three-way PATCH semantics instead of jackson-databind-nullable (which targets Jackson 2.x and is incompatible with Spring Boot 4.0 / Jackson 3.x). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
44 lines
2.0 KiB
Markdown
44 lines
2.0 KiB
Markdown
# ADR-035 — `Optional<String>` for three-way PATCH semantics
|
|
|
|
**Status:** Accepted
|
|
**Date:** 2026-06-08
|
|
**Issue:** #751 (JourneyItem CRUD API)
|
|
|
|
## Context
|
|
|
|
The `PATCH /api/geschichten/{id}/items/{itemId}` endpoint must distinguish three cases for the `note` field:
|
|
|
|
| JSON body | Intended meaning |
|
|
|-------------------|-----------------------|
|
|
| `{"note": "text"}`| Set note to "text" |
|
|
| `{"note": null}` | Clear the note |
|
|
| `{}` (absent) | Leave note unchanged |
|
|
|
|
The standard library for this on Jackson 2.x is `jackson-databind-nullable` (`JsonNullable<T>` from `org.openapitools`). However, that library targets `com.fasterxml.jackson.*` (Jackson 2.x) and is incompatible with Spring Boot 4.0 / Spring Framework 7, which uses `tools.jackson.*` (Jackson 3.x). The module fails to register and throws at startup.
|
|
|
|
## Decision
|
|
|
|
Use `Optional<String>` with Java's default field initializer (`= null`) to encode the three states:
|
|
|
|
```java
|
|
@Data
|
|
public class JourneyItemUpdateDTO {
|
|
private Optional<String> note = null; // Java default — absent = no-op
|
|
}
|
|
```
|
|
|
|
| Java value | JSON wire | Semantics |
|
|
|--------------------|-------------------|---------------|
|
|
| `null` (default) | field absent | no-op |
|
|
| `Optional.empty()` | `{"note": null}` | clear |
|
|
| `Optional.of("x")` | `{"note": "x"}` | set |
|
|
|
|
Jackson 3.x natively maps a JSON `null` to `Optional.empty()` and leaves absent fields at their Java default. No custom module is needed.
|
|
|
|
## Consequences
|
|
|
|
- No external dependency for PATCH semantics — simpler pom.xml.
|
|
- The DTO field type is `Optional<String>`, not `String` — service code must null-check the field first (`if (noteField == null) return;`) and then call `.orElse(null)` to unwrap.
|
|
- This pattern applies to any future PATCH DTO that needs three-way semantics on a nullable field.
|
|
- `jackson-databind-nullable` is removed from `pom.xml`; `JacksonConfig.java` is kept as a placeholder for future custom modules.
|