-
Notifications
You must be signed in to change notification settings - Fork 324
Description
8
The Problem: The Synchronization Tax
Spring for GraphQL’s SDL-First mandate forces a manual "Double-Entry" loop. This creates a fragmented development experience where third-party generators (DGS) are disconnected from the Request Execution Flow.
1. The Resolver Contradiction (The "Non-Null" Trap)
In GraphQL, a field can be Non-Null in the schema but resolved via a separate @schemamapping.
Example Schema:
type User {
id: ID!
username: String!
reputationScore: Int! # Non-Null Schema Field
}- The Conflict: DGS generates "record User(String id, String username, Integer reputationScore)". Because it’s Int!, the Record enforces a non-null reputationScore in the constructor.
- The Breakdown: If reputationScore is resolved via a separate service call, the User object must be instantiated WITHOUT that score first to be passed to the field resolver. The strict Record makes this impossible.
- The Validation Sacrifice: You are forced to make the DTO field @nullable just to allow instantiation. Now, you’ve lost the ability to use Jakarta to validate that the data-backed fields (id, username) are present, because the DTO is now a "leaky" bucket.
@Controller
public class UserController {
// The "Partial" DTO hack required because the generator-produced
// Record is too strict for the execution flow.
public record UserPartial(String id, String username) {}
@QueryMapping
public UserPartial userById(@Argument String id) {
return new UserPartial(id, "JavaEnthusiast");
}
@SchemaMapping(typeName = "User", field = "reputationScore")
public int getReputationScore(UserPartial parent) {
// This is called lazily. We use the parent ID to fetch the score.
return reputationService.getScoreForUser(parent.id());
}
}2. The Validation Pipeline Gap
DGS-generated Records trigger Objects.requireNonNull in the constructor, causing a hard NPE BEFORE Jakarta Validation can run.
- The Result: The framework cannot collect multiple ConstraintViolations because instantiation fails immediately on the first null.
- The "Apathy" Loop: To get Jakarta validation, developers must use "soft" POJOs with nulls, defeating the purpose of strict Java Records.
The Solution: Decoupling Validation from Instantiation
Path A: Standards-Based Code-First (MicroProfile)
Supporting MicroProfile GraphQL makes Java Records the "Source of Truth."
- Pre-Instantiation Validation: Validates raw input against the schema BEFORE the constructor is called.
- Cohesive Validation: Allows for strict Records that are only instantiated once the entire input set is deemed valid.
Path B: A "Smart" Binding-Aware Generator
Alternatively, a generator designed for the Spring/Jakarta flow:
- Contextual Nullability: Recognizes that "Non-Null" in SDL doesn't always mean "Non-Null" in the initial DTO if a resolver is present.
- Partial Mapping Hooks: Generates models that support @schemamapping without requiring a second, manual DTO.
Conclusion
Spring should offer a native way to decouple input validation from instantiation, supporting strict Java Records without breaking the API contract or forcing "Double DTO" hacks.
Reference
- MicroProfile GraphQL Spec: https://microprofile.io/specifications/graphql/