Skip to main content
The Grounds JVM module library uses typed service keys so modules can publish stable contracts and other modules can consume them without raw-string lookups. Use the registry when modules in the same JVM share a stable interface, such as a matchmaking service, player service, configuration service, or NATS client. It is not remote service discovery and does not create classloader isolation.

Type-first access

Use type-first access when one implementation provides a service contract.
The default serviceKey<T>() uses the Kotlin type as the key. This keeps normal call sites free from string identifiers.

Class-based access

Use class-based overloads from non-reified code, Java-facing adapters, or code that already has a KClass.
These overloads create the same default service key as serviceKey<MatchmakingService>().

Optional services

Use get or contains when a service is optional.
Use require for mandatory dependencies. It throws MissingServiceException when no service is registered, so missing contracts fail during startup rather than later gameplay.

Qualified keys

Use qualified keys only when several services implement the same public contract and consumers must select one explicitly.
Register and read qualified keys explicitly:
Do not create qualified keys inline at call sites. Define them once in the module API that owns the contract, then reuse those constants everywhere.

Module descriptors

Use the same service keys in module descriptors. requires and provides describe service contracts; dependsOn declares ordering that has no service contract.

Failure behavior

The default registry rejects duplicate registration for the same service key. If two implementations of one contract must exist, give each one a stable, qualified key.
Registering the same key twice throws DuplicateServiceException.
registry.require<MatchmakingService>() throws MissingServiceException when the service is not registered. Prefer require for mandatory module dependencies.

Next steps