Tenants
A module:
- Encapsulates its GraphQL SDL (
*.graphqls) under a fixed directory. - Owns the resolvers that implement the fields declared in that SDL.
- Contributes its slice of the overall app surface, which is later assembled into a central schema.
The real “visibility” and “security” behavior comes from schemas and scopes. Modules help you organize business subdomains cleanly; schemas/scopes decide what is exposed at runtime.
StarWars modules¶
universe¶
Focus: planets, species, starships, vehicles, and their relationships.
Common fields and patterns:
Planet.id,Planet.name,Species.id,Species.name, and the starship and vehicle fields.
filmography¶
Focus: characters, the catalog of films, and character appearances. The character resolvers also refer to types owned by universe, such as a character's homeworld or species.
Common fields and patterns:
Film.id,Film.title,Film.releaseDate.Film.characters(list of appearing characters).Character.id,Character.name,Character.homeworld,Character.species.
Where module SDL lives¶
Each module keeps its schema files in a stable location so the build can find them:
Concrete examples from this repo:
modules/filmography/src/main/viaduct/schema/Character.graphqls
modules/filmography/src/main/viaduct/schema/Film.graphqls
modules/universe/src/main/viaduct/schema/Planet.graphqls
modules/universe/src/main/viaduct/schema/Species.graphqls
modules/universe/src/main/viaduct/schema/Starship.graphqls
modules/universe/src/main/viaduct/schema/Vehicle.graphqls
During the build, these are transformed into a central schema that merges all partitions.
Where module resolvers live¶
Resolvers for each module are implemented in the module’s language-specific source directory.
Concrete examples (filmography):
modules/filmography/src/main/kotlin/com/example/starwars/modules/filmography/films/queries/AllFilmsQueryResolver.kt
modules/filmography/src/main/kotlin/com/example/starwars/modules/filmography/films/resolvers/FilmNodeResolver.kt
modules/filmography/src/main/kotlin/com/example/starwars/modules/filmography/films/resolvers/CharactersFieldResolver.kt
modules/filmography/src/main/kotlin/com/example/starwars/modules/filmography/films/resolvers/PlanetsFieldResolver.kt
modules/filmography/src/main/kotlin/com/example/starwars/modules/filmography/characters/resolvers/FilmCountBatchResolver.kt
modules/filmography/src/main/java/com/example/starwars/modules/filmography/films/queries/AllFilmsQueryResolver.java
modules/filmography/src/main/java/com/example/starwars/modules/filmography/films/resolvers/FilmNodeResolver.java
modules/filmography/src/main/java/com/example/starwars/modules/filmography/films/resolvers/FilmCharactersResolver.java
modules/filmography/src/main/java/com/example/starwars/modules/filmography/films/resolvers/FilmPlanetsResolver.java
modules/filmography/src/main/java/com/example/starwars/modules/filmography/characters/resolvers/CharacterFilmCountResolver.java
The Java module plugin generates Java GRTs and resolver base classes before Java compilation. Its annotation processor records resolver and document metadata for runtime discovery.
The Kotlin module plugin generates resolver base classes you extend, under:
For example, Kotlin generates CharacterResolvers.kt, FilmResolvers.kt, QueryResolvers.kt, and NodeResolvers.kt. Java generates corresponding .java base classes in each configured module package, such as com.example.starwars.filmography.resolverbases.
How modules are assembled¶
The builder :
- Discovers all SDL files per module (schema partitions).
- Registers schema IDs that point to a concrete surface (public vs extras).
- At runtime, the controller serves each
schemaIdon its own route and builds anExecutionInput. - Viaduct executes the request against that schema, which contains only the fields its scopes allow.
See the Schemas and Scope pages for runtime selection and visibility rules.
What belongs in a module¶
- SDL files (
*.graphqls) for the domain’s types, queries, and directives (use@scope,@idOf,@oneOfwhere appropriate). - Resolvers implementing the domain’s behavior (
queryresolvers,fieldresolvers,batchresolvers,mutations). - Tests providing integration coverage for the module’s surface.
For the full multi-tenancy model — how tenant modules register schemas and resolvers in a shared Viaduct instance — see the Multi-tenancy service engineer docs.