Starter services
View MarkdownStatus: Stable Package:
dev.vertique.starter.servicesArtifact:vertique-starter-servicesDepends on: starter-core, management, services API reference: javadoc.io
The vertique-starter-services module publishes one public Dagger aggregate,
ServicesApplicationModule, that composes Vertique’s contract-based service execution foundation
on top of the core application starter. An application names the aggregate in its @Component
instead of repeating the dispatch and management module list in every generated application.
This module is a composition surface only. It declares no bindings of its own, contributes no deployment entry, transport, test, or generated code, and adds no runtime behavior beyond what the modules it includes already provide.
When To Use It
Section titled “When To Use It”Install vertique-starter-services when an application defines typed @ServiceContract
interfaces and wants Vertique to manage their event-bus dispatch, service verticle lifecycle,
supervision, and management wiring. It already includes CoreApplicationModule, so an application
that names this aggregate does not also name the core starter.
Core Concepts
Section titled “Core Concepts”The aggregate is exactly three modules
Section titled “The aggregate is exactly three modules”ServicesApplicationModule includes exactly:
| Included module | Artifact | What it brings |
|---|---|---|
dev.vertique.starter.core.CoreApplicationModule |
vertique-starter-core |
the host-neutral lifecycle foundation — Vert.x seam, config parsing, verticle deployment, core lifecycle steps |
dev.vertique.services.DispatchModule |
vertique-services |
the event-bus dispatch runtime — the typed services config boundary, contract registry, target resolver, supervisor, service exception mapper, ServiceClientFactory, and the paired SERVICES-phase deployment lifecycle steps |
dev.vertique.management.ManagementModule |
vertique-management |
ManagementConfig, the health-check multibindings, and the management endpoint contributor set |
Dagger applies @Module(includes = …) recursively, so a component that names
ServicesApplicationModule reaches all three — and everything they include in turn — transitively.
VertxModule arrives through CoreApplicationModule; it is stateful and therefore remains a
generated-builder input, so the generated application factory still calls
.vertxModule(new VertxModule(runtime.vertx(), runtime.config())).
Membership is a compatibility surface
Section titled “Membership is a compatibility surface”Both the included-module membership above and this module’s direct dependency ledger are release-line compatibility surfaces. Consumers may rely on the aggregate resolving the bindings those three modules declare, and on the aggregate pulling in nothing beyond its declared ledger. Changes to either are treated as compatibility-affecting, not as internal refactoring.
The default execution model is the event loop
Section titled “The default execution model is the event loop”Service implementations reached through this aggregate run on the Vert.x event loop by default.
Worker opt-in is a per-contract application decision expressed in configuration
(services.contracts.<namespace>.<name>.worker=true) and is appropriate only for implementations
that perform genuinely blocking work. The starter sets no such flag and makes no execution-model
choice on the application’s behalf.
What applications still own
Section titled “What applications still own”The starter deliberately stops at composition. Applications remain responsible for:
- Management deployment entries — the
@Provides @IntoSet VerticleDeploymentthat deploys the management verticle. The starter contributes none, so an application that names only this aggregate builds a complete graph but exposes no management port. Service verticles themselves are deployed by theSERVICES-phase startup stepDispatchModulecontributes, not by an application deployment entry. - Generated services module — the annotation-processor output module carrying server contributor
bindings and injectable typed-client bindings is named explicitly in the component. The generated
client provider still delegates to
ServiceClientFactory; the starter does not create proxies. - Launcher choice —
vertique-launcheris not a dependency of this module. - Test libraries —
vertique-application-testand JUnit stay explicit test-scope dependencies of the application. - Worker opt-in — see the execution-model note above.
Key Classes
Section titled “Key Classes”ServicesApplicationModule
Section titled “ServicesApplicationModule”Public abstract Dagger module. It declares no constructor, field, method, nested type, or scope — downstream components name the class, and neither Dagger nor application code instantiates it.
@VertiqueApp@Singleton@Component( modules = { ServicesApplicationModule.class, AppModule.class, GeneratedServicesModule.class })interface AppComponent extends VertiqueApplicationComponent { GreetingService greetingService();}AppModule is the application-owned module contributing its management deployment entry;
GeneratedServicesModule stands for whichever module the Vertique annotation processors generate
for that application. It contributes the server registrations and provides each eligible service
contract as a singleton typed client, so application classes inject GreetingService directly.
Extension Points
Section titled “Extension Points”This module publishes no extension point of its own. Extend the application through the
multibindings its included modules declare — among them @Services Set<Object>,
Set<ServiceInterceptor>, Set<ServiceExceptionMapperCustomizer>,
Set<ServiceContractContributor> (from dev.vertique:vertique-services),
@Liveness Set<HealthCheck>, @Readiness Set<HealthCheck>,
Set<ManagementEndpointContributor> (from dev.vertique:vertique-management), and the
Set<VerticleDeployment>, Set<ApplicationStartupStep>, Set<ApplicationShutdownStep> sets the
core starter reaches — by contributing @Provides @IntoSet methods from an application-owned
module.
Common Mistakes
Section titled “Common Mistakes”- Expecting the starter to deploy the management endpoint. No
VerticleDeploymentis contributed here, so an application that names onlyServicesApplicationModulestarts with an empty deployment set and exposes no management port. Service verticles are deployed by theSERVICES-phase startup step, which is contributed. - Expecting the generated module to be included. The annotation-processor output module is never reachable from a framework aggregate; it must be listed in the component explicitly. Once included, eligible service contracts are injectable without an application-owned provider.
- Naming
CoreApplicationModulealongside this aggregate. The core starter is already reached transitively; listing it again adds nothing. - Marking services as workers by default. Worker opt-in is per contract and only for genuinely blocking implementations; the default execution model is the event loop.
- Expecting the starter to supply a launcher. It does not depend on
dev.vertique:vertique-launcher. A standalone application still declares the launcher itself.
Dependencies
Section titled “Dependencies”dev.vertique:vertique-starter-core—CoreApplicationModule, and through it theVertiqueApplicationComponentlifecycle contract consuming components extenddev.vertique:vertique-management—ManagementModuledev.vertique:vertique-services—DispatchModulecom.google.dagger:dagger— the@Moduleannotation itself
The ledger above is exact: the module declares no other direct dependency. Database, launcher, test, code-generation, and other capability artifacts remain application-owned composition choices.
Verification
Section titled “Verification”ServicesApplicationModule is proven by the starter family’s integration-test harness, which
compiles a real @VertiqueApp component naming only this aggregate, builds it through the
generated application factory, and asserts that the ServiceClientFactory, paired service
deployment lifecycle steps, and management binding resolve. A dependency fixture materializes the
compile and runtime classpaths, enforces the exact framework-artifact ledger, and applies targeted
forbidden-category checks so the bounded starter dependency closure cannot drift silently.