Skip to content

Commit 10f5857

Browse files
authored
Adding a Micronaut example (#584)
1 parent 150af31 commit 10f5857

38 files changed

+3274
-2
lines changed

.cursor/agents/robot-coordinator.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
22
name: robot-coordinator
33
model: inherit
4-
description: Tech lead orchestrator for Java Enterprise Development. Identifies framework from requirements, delegates to java-developer, spring-boot-developer, quarkus-coder, or micronaut-coder via plan task tables and the Parallel column; never implements code itself.
4+
description: Coordinator for Java Enterprise Development. Identifies framework from requirements, delegates to java-developer, spring-boot-developer, quarkus-coder, or micronaut-coder via plan task tables and the Parallel column; never implements code itself.
55
---
66

7-
You are a **Tech Lead Orchestrator** for Java Enterprise Development. Your primary responsibility is to coordinate technical work by **delegating** to specialized agents and **synthesizing** their outputs.
7+
You are a **Coordinator** for Java Enterprise Development. Your primary responsibility is to coordinate technical work by **delegating** to specialized agents and **synthesizing** their outputs.
88

99
### Core role (non-negotiable)
1010

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Thumbs.db
2+
.DS_Store
3+
.gradle
4+
build/
5+
target/
6+
out/
7+
.micronaut/
8+
.idea
9+
*.iml
10+
*.ipr
11+
*.iws
12+
.project
13+
.settings
14+
.classpath
15+
.factorypath
16+
tomcat/
Binary file not shown.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.13/apache-maven-3.9.13-bin.zip
18+
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Greek Gods API (US-001) — Micronaut
2+
3+
Micronaut 4.x service: **`GET /api/v1/gods/greek`** returns a JSON array of Greek god names from PostgreSQL (read path never calls the upstream JSON server). Background sync upserts names from a configurable upstream base URL + **`GET /greek`**.
4+
5+
## Run
6+
7+
Prerequisites: JDK 21+, Maven (wrapper included), local PostgreSQL (or override URL via env).
8+
9+
```bash
10+
./mvnw mn:run
11+
```
12+
13+
Default datasource in `src/main/resources/application.yml` targets `jdbc:postgresql://localhost:5432/greek_gods` with user/password `postgres` / `postgres`. Override using Micronaut environment variables, for example:
14+
15+
- `DATASOURCES_DEFAULT_URL`
16+
- `DATASOURCES_DEFAULT_USERNAME`
17+
- `DATASOURCES_DEFAULT_PASSWORD`
18+
19+
Upstream sync (no hard-coded production URL in code):
20+
21+
- `GREEK_GODS_UPSTREAM_BASE_URL` — overrides `greek-gods.upstream.base-url` (JDK `HttpClient` calls `{base-url}/greek`).
22+
23+
## Build and tests
24+
25+
```bash
26+
./mvnw clean verify
27+
```
28+
29+
- **Unit tests** (`*Test`): Surefire — no Docker.
30+
- **Integration tests** (`*IT`): Failsafe — Testcontainers PostgreSQL, WireMock for sync; require Docker.
31+
32+
Skip integration tests (faster local iteration):
33+
34+
```bash
35+
./mvnw clean verify -DskipITs=true
36+
```
37+
38+
or use the Maven profile:
39+
40+
```bash
41+
./mvnw clean verify -Pfast
42+
```
43+
44+
Run a subset of scenarios by JUnit tag (Failsafe / Surefire):
45+
46+
```bash
47+
./mvnw -q verify -Dgroups=smoke
48+
./mvnw -q verify -Dgroups=error-handling
49+
```
50+
51+
## JUnit tags (Gherkin mapping)
52+
53+
| Tag | Meaning |
54+
|-----|---------|
55+
| `smoke`, `happy-path` | 20 canonical names, ordered, set equality |
56+
| `performance` | Single GET under 1 second |
57+
| `load-testing` | 100 concurrent GETs, wall clock under 2 seconds |
58+
| `error-handling` | 500 `application/problem+json` (normative fields); empty DB → `[]` |
59+
| `data-quality` | After WireMock stubbed sync, API matches stub payload |
60+
| `api-specification` | `Content-Type` includes `application/json`; JSON array of strings |
61+
62+
**`@availability`** from the feature file is **not** automated (SLO/monitoring only).
63+
64+
### CI flake note (`load-testing`)
65+
66+
The **`load-testing`** scenario enforces **100 concurrent** requests within **2 seconds** total wall time. Shared CI agents can occasionally miss this window; if the build fails only on this test, re-run the job or use a larger runner. Tuning: increase the threshold only if product owners agree to relax the Gherkin timing.
67+
68+
### Error-handling IT
69+
70+
`GreekGodsDatabaseFailureIT` uses **`@MockBean` on `GreekGodRepository`** to simulate a read-path persistence failure while Flyway still runs against a real Testcontainers database. The HTTP response still satisfies ADR-001 (500 + `type`, `title`, `status`).
71+
72+
## OpenAPI
73+
74+
Runtime annotations align with `requirements3/design/greekController-oas.yaml`. After `./mvnw compile`, the generated spec is at `target/classes/META-INF/swagger/greek-gods-api-1.0.0.yml`.
75+
76+
## Scheduling
77+
78+
`@Scheduled` is provided by **`micronaut-context`** (transitive). There is no separate `micronaut-scheduling` artifact on the 4.10 BOM. Sync timing is configured with:
79+
80+
- `greek-gods.sync.fixed-delay` (default `5m`)
81+
- `greek-gods.sync.initial-delay` (default `30s`)
82+
- `greek-gods.sync.scheduler-enabled` (`false` disables the job bean)
83+
- `greek-gods.sync.enabled` (skips work inside the job when `false`)
84+
85+
## HTTP client note
86+
87+
Upstream sync uses **`java.net.http.HttpClient`** (blocking `send`) with **`greek-gods.upstream.connect-timeout`** and **`read-timeout`** — no Micronaut **`micronaut-http-client`** / Netty on the outbound path.
88+
89+
### Why `micronaut-core-reactive` stays on the classpath
90+
91+
There is **no supported way** to run this app with **Micronaut’s HTTP server** (embedded Tomcat here) and **omit** `micronaut-core-reactive`:
92+
93+
- Server-agnostic response encoding lives in **`io.micronaut.http.server.ResponseLifecycle`** (Micronaut 4.8+), used by both Netty and servlet runtimes.
94+
- That class imports **`io.micronaut.core.async.publisher.Publishers`** and **`LazySendingSubscriber`**, which are packaged **only** in **`micronaut-core-reactive`**, not in `micronaut-core`.
95+
- **`micronaut-http-server`**’s POM does not list `micronaut-core-reactive`, so many apps only got it **transitively** (e.g. via `micronaut-http-client`). This module declares it **explicitly** so the app still starts after removing the declarative client.
96+
97+
So: **your controllers and upstream sync stay imperative/blocking**, but the framework still loads small reactive helper types for **response encoding**. Avoiding that JAR entirely would mean **not using Micronaut’s HTTP stack** (different framework or a non-HTTP entrypoint). Upstream improvement would be for `micronaut-http-server` to declare this dependency so every app gets a correct classpath by default ([micronaut-core](https://github.com/micronaut-projects/micronaut-core) `ResponseLifecycle`).
98+
99+
---
100+
101+
Micronaut reference: [User Guide](https://docs.micronaut.io/4.10.10/guide/index.html)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# AOT configuration properties for jar packaging
2+
# Please review carefully the optimizations enabled below
3+
# Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details
4+
5+
# Caches environment property values: environment properties will be deemed immutable after application startup.
6+
cached.environment.enabled=true
7+
8+
# Precomputes Micronaut configuration property keys from the current environment variables
9+
precompute.environment.properties.enabled=true
10+
11+
# Replaces logback.xml with a pure Java configuration
12+
logback.xml.to.java.enabled=true
13+
14+
# Converts configuration files from YAML and properties to Java configuration
15+
property-source-loader.generate.enabled=true
16+
17+
# Scans for service types ahead-of-time, avoiding classpath scanning at startup
18+
serviceloading.jit.enabled=true
19+
20+
# Scans reactive types at build time instead of runtime
21+
scan.reactive.types.enabled=true
22+
23+
# Deduces the environment at build time instead of runtime
24+
deduce.environment.enabled=true
25+
26+
# Checks for the existence of some types at build time instead of runtime
27+
known.missing.types.enabled=true
28+
29+
# Precomputes property sources at build time
30+
sealed.property.source.enabled=true
31+
32+
# The list of service types to be scanned (comma separated)
33+
service.types=io.micronaut.context.env.PropertySourceLoader,io.micronaut.inject.BeanConfiguration,io.micronaut.inject.BeanDefinitionReference,io.micronaut.http.HttpRequestFactory,io.micronaut.http.HttpResponseFactory,io.micronaut.core.beans.BeanIntrospectionReference,io.micronaut.core.convert.TypeConverterRegistrar,io.micronaut.context.env.PropertyExpressionResolver
34+
35+
# A list of types that the AOT analyzer needs to check for existence (comma separated)
36+
known.missing.types.list=io.reactivex.Observable,reactor.core.publisher.Flux,kotlinx.coroutines.flow.Flow,io.reactivex.rxjava3.core.Flowable,io.reactivex.rxjava3.core.Observable,io.reactivex.Single,reactor.core.publisher.Mono,io.reactivex.Maybe,io.reactivex.rxjava3.core.Single,io.reactivex.rxjava3.core.Maybe,io.reactivex.Completable,io.reactivex.rxjava3.core.Completable,io.methvin.watchservice.MacOSXListeningWatchService,io.micronaut.core.async.publisher.CompletableFuturePublisher,io.micronaut.core.async.publisher.Publishers.JustPublisher,io.micronaut.core.async.subscriber.Completable
37+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
applicationType: default
2+
defaultPackage: info.jab.ms.gods
3+
testFramework: junit
4+
sourceLanguage: java
5+
buildTool: maven
6+
features: [app-name, http-client-test, java, java-application, junit, logback, maven, maven-enforcer-plugin, micronaut-aot, micronaut-http-validation, micronaut-processing-compiler-args, netty-server, properties, readme, serialization-jackson, shade, static-resources]

0 commit comments

Comments
 (0)