Architecture walkthrough · Quarkus Todo
How I structure my applications.
A small task manager, used to explain the decisions behind the code. Follow the dependencies from the clients to the database, one diagram at a time.
A familiar problem,
three applications.
Quarkus Todo brings together a Java 17 backend, a React web client built with Vite, and a React Native application built with Expo. The clients support task creation, completion, deletion, search and filtering, in English, French and Spanish.
This application reflects how I usually structure my projects: clear responsibilities, business logic separated from the UI, and external integrations behind adapters. Its deliberately small scope makes those choices easier to follow.
Both clients call Quarkus for tasks and weather. Quarkus accesses H2 and calls OpenWeatherMap through a backend adapter; the API key stays on the server. The diagram shows real integration mode; mocks replace external calls during offline development.
Give each layer
a clear responsibility.
Components focus on rendering and interaction. Contexts and hooks coordinate the screen. Services handle domain operations, while adapters communicate with external systems.
React Query manages fetching and cache invalidation. Completing or deleting a task updates the interface optimistically; failed requests restore the previous state, then the client refreshes against the server.
The web and mobile clients mirror this structure. Their implementations are currently separate, rather than a shared package. Pure filtering logic lives in TodoFilterService, outside the API path shown here.
Change the integration.
Keep the interface.
TodoService depends on ITodoAdapter. The real adapter wraps a TypeScript client generated from Quarkus’s OpenAPI description. The mock adapter provides an in-memory store for offline development.
Environment settings select the implementation. The service does not need to know whether a backend is running. Weather has its own adapter boundary and can be simulated independently. Real client adapters call /api/v1/weather; the backend OpenWeatherAdapter handles the provider response and request timeout.
Vitest and Jest cover domain logic, services and components. Playwright checks the web interface against mock adapters; it does not claim to validate the Java API. Maestro flows cover mobile journeys when a device or simulator is available.
From HTTP
to stored tasks.
TodoController translates request and response objects. TodoService coordinates operations and transactions. TodoRepository uses Panache and Hibernate ORM to access the database.
H2 currently runs in memory. Tasks survive browser reloads, but restarting the backend resets them. React Query’s cache improves the interface; it is not the source of persistent data.
A Quarkus integration test exercises creation, retrieval, modification and deletion through HTTP. Durable storage, stronger input validation and automated tests against the real API are the next useful steps.
A place for each responsibility.
com.example.api.v1Versioned public API
Groups the public HTTP contract. I usually document these endpoints with OpenAPI and Swagger UI.
Todo already uses api.v1. Package names organize code; authentication and authorization determine who can access an endpoint.
Choose the implementation.
Keep the service independent.
In my usual architecture, I place persistence behind an adapter interface. The service depends on that contract, while a profile selects a real implementation or a mock.
The real adapter delegates to a repository such as TodoRepository. Switching between H2 and another supported database can keep that implementation and change the datasource configuration. Selecting a mock is a separate strategy.
An in-process mock adapter provides controlled data and failures for hand-written Cucumber scenarios. No HTTP stub server is needed for this persistence boundary. Tests against a real database remain necessary to validate mappings, queries and transactions.
This diagram illustrates my usual approach, not the current Todo implementation. TodoService currently calls TodoRepository directly; this persistence boundary and the Cucumber suite remain to be added. The interface and adapter names shown are illustrative.
Keep the contract.
Control the dependency.
I apply the same principle to external APIs: a service depends on a weather interface, while an adapter handles HTTP, provider data and failures. A profile can select a mock adapter for isolated tests.
To test the real HTTP adapter, I keep it in place and point its base URL at a stub server instead of OpenWeatherMap. This exercises requests, response mapping and error handling without depending on the live provider.
Cucumber describes the scenarios and assertions; the stub supplies the HTTP responses. For example, an upstream 503 should produce a sanitized 502 from our API. A mock adapter alone would not exercise that HTTP boundary.
In Todo today, WeatherController calls OpenWeatherAdapter directly. The backend owns the key, validates coordinates and applies a request timeout. JUnit tests already use a local HTTP stub to check successful mapping, upstream failures and malformed data.
The WeatherService, WeatherPort, profile-selected MockWeatherAdapter and Cucumber layer shown here are proposed extensions. The existing client-side IWeatherAdapter and offline mocks are a separate boundary.
Generate the plumbing.
Focus on the application.
Quarkus exposes its OpenAPI document. With openapi-typescript-codegen, I generate TypeScript models, API service methods and request helpers: an Axios client for React and a Fetch client for Expo.
This automates repetitive API integration code. Screens, business rules, hooks and adapter boundaries remain hand-written. The automation covers the API client layer, while application-specific behavior stays under my control.
TanStack Query manages server state at runtime. Both clients use the todos query key to cache task data, expose loading and error states, and synchronize mutations with the server. Optimistic updates can be rolled back when a request fails.
Regeneration helps surface contract changes through TypeScript; it still requires review and tests. The new weather adapter currently uses a hand-written fetch call to Quarkus. Expo also includes a separate project script for generating task mocks from the API description.
The result is less repetitive code and a consistent integration approach across web and mobile, while keeping application behavior explicit and testable.