Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Quarkus 3.x LTS architecture patterns with Camel for messaging, RESTful API design, CDI services, data access with Panache, and async processing. Use for Java Quarkus backend work with event-driven architectures.
.claude/skills/quarkus-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | — | — |
| case-08 | ✗→✓ | ▲ Improved | — | — |
| case-17 | ✗→✓ | ▲ Improved | — | — |
| case-03 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✓ | ▲ Improved | — | — |
Quarkus 3.x architecture and API patterns for cloud-native, event-driven services with Apache Camel.
java@Slf4j @ApplicationScoped @RequiredArgsConstructor public class OrderProcessingService { private final OrderValidator orderValidator; private final EventService eventService; private final OrderRepository orderRepository; private final FulfillmentPublisher fulfillmentPublisher; private final AuditPublisher auditPublisher; @Transactional public OrderReceipt process(CreateOrderCommand command) { ValidationResult validation = orderValidator.validate(command); if (!validation.valid()) { eventService.createErrorEvent(command, "ORDER_REJECTED", validation.message()); throw new WebApplicationException(validation.message(), Response.Status.BAD_REQUEST); } Order order = Order.from(command); orderRepository.persist(order); OrderReceipt receipt = OrderReceipt.from(order); fulfillmentPublisher.publishAsync(receipt); auditPublisher.publish("ORDER_ACCEPTED", receipt); eventService.createSuccessEvent(receipt, "ORDER_ACCEPTED"); log.info("Processed order {}", order.id); return receipt; } }
Key Patterns:
@RequiredArgsConstructor for constructor injection via Lombok@Slf4j for Logback logging@Transactional on service methods that write through Panache or repositoriesjava@ApplicationScoped public class ProcessingService { public void processDocument(Document doc) { LogContext logContext = CustomLog.getCurrentContext(); try (SafeAutoCloseable ignored = CustomLog.startScope(logContext)) { // Add context to all log statements logContext.put("documentId", doc.getId().toString()); logContext.put("documentType", doc.getType()); logContext.put("userId", SecurityContext.getUserId()); log.info("Starting document processing"); // All logs within this scope inherit the context processInternal(doc); log.info("Document processing completed"); } catch (Exception e) { log.error("Document processing failed", e); throw e; } } }
Logback Configuration (logback.xml):
xml<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="net.logstash.logback.encoder.LogstashEncoder"> <includeContext>true</includeContext> <includeMdc>true</includeMdc> </encoder> </appender> <logger name="com.example" level="INFO"/> <root level="WARN"> <appender-ref ref="CONSOLE"/> </root> </configuration>
java@Slf4j @ApplicationScoped @RequiredArgsConstructor public class EventService { private final EventRepository eventRepository; private final ObjectMapper objectMapper; public void createSuccessEvent(Object payload, String eventType) { Objects.requireNonNull(payload, "Payload cannot be null"); Event event = new Event(); event.setType(eventType); event.setStatus(EventStatus.SUCCESS); event.setPayload(serializePayload(payload)); event.setTimestamp(Instant.now()); eventRepository.persist(event); log.info("Success event created: {}", eventType); } public void createErrorEvent(Object payload, String eventType, String errorMessage) { Objects.requireNonNull(payload, "Payload cannot be null"); if (errorMessage == null || errorMessage.isBlank()) { throw new IllegalArgumentException("Error message cannot be blank"); } Event event = new Event(); event.setType(eventType); event.setStatus(EventStatus.ERROR); event.setErrorMessage(errorMessage); event.setPayload(serializePayload(payload)); event.setTimestamp(Instant.now()); eventRepository.persist(event); log.error("Error event created: {} - {}", eventType, errorMessage); } private String serializePayload(Object payload) { try { return objectMapper.writeValueAsString(payload); } catch (JsonProcessingException e) { throw new IllegalStateException("Failed to serialize event payload", e); } } }
java@Slf4j @ApplicationScoped @RequiredArgsConstructor public class BusinessRulesPublisher { private final ProducerTemplate producerTemplate; public void publishSync(BusinessRulesPayload payload) { producerTemplate.sendBody( "direct:business-rules-publisher", payload ); } }
Camel Route Configuration:
java@ApplicationScoped public class BusinessRulesRoute extends RouteBuilder { @ConfigProperty(name = "camel.rabbitmq.queue.business-rules") String businessRulesQueue; @ConfigProperty(name = "rabbitmq.host") String rabbitHost; @ConfigProperty(name = "rabbitmq.port") Integer rabbitPort; @Override public void configure() { from("direct:business-rules-publisher") .routeId("business-rules-publisher") .log("Publishing message to RabbitMQ: ${body}") .marshal().json(JsonLibrary.Jackson) .toF("spring-rabbitmq:%s?hostname=%s&portNumber=%d", businessRulesQueue, rabbitHost, rabbitPort); } }
java@ApplicationScoped public class DocumentProcessingRoute extends RouteBuilder { @Override public void configure() { // Error handling onException(ValidationException.class) .handled(true) .to("direct:validation-error-handler") .log("Validation error: ${exception.message}"); // Main processing route from("direct:process-document") .routeId("document-processing") .log("Processing document: ${header.documentId}") .bean(DocumentValidator.class, "validate") .bean(DocumentTransformer.class, "transform") .choice() .when(header("documentType").isEqualTo("INVOICE")) .to("direct:process-invoice") .when(header("documentType").isEqualTo("CREDIT_NOTE")) .to("direct:process-credit-note") .otherwise() .to("direct:process-generic") .end(); from("direct:validation-error-handler") .bean(EventService.class, "createErrorEvent") .log("Validation error handled"); } }
java@ApplicationScoped public class FileMonitoringRoute extends RouteBuilder { @ConfigProperty(name = "file.input.directory") String inputDirectory; @ConfigProperty(name = "file.processed.directory") String processedDirectory; @ConfigProperty(name = "file.error.directory") String errorDirectory; @Override public void configure() { from("file:" + inputDirectory + "?move=" + processedDirectory + "&moveFailed=" + errorDirectory + "&delay=5000") .routeId("file-monitor") .log("Processing file: ${header.CamelFileName}") .to("direct:process-file"); from("direct:process-file") .bean(OrderProcessingService.class, "processFile") .log("File processing completed"); } }
java@ApplicationScoped public class InvoiceRoute extends RouteBuilder { @Override public void configure() { from("direct:invoice-validation") .bean(InvoiceFlowValidator.class, "validateFlowWithConfig") .log("Validation result: ${body}"); from("direct:persist-and-publish") .bean(DocumentJobService.class, "createDocumentAndJobEntities") .bean(BusinessRulesPublisher.class, "publishAsync") .bean(EventService.class, "createSuccessEvent(${body}, 'PUBLISHED')"); } }
java@Path("/api/documents") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @RequiredArgsConstructor public class DocumentResource { private final DocumentService documentService; @GET public Response list( @QueryParam("page") @DefaultValue("0") int page, @QueryParam("size") @DefaultValue("20") int size) { List<Document> documents = documentService.list(page, size); return Response.ok(documents).build(); } @POST public Response create(@Valid CreateDocumentRequest request, @Context UriInfo uriInfo) { Document document = documentService.create(request); URI location = uriInfo.getAbsolutePathBuilder() .path(String.valueOf(document.id)) .build(); return Response.created(location).entity(DocumentResponse.from(document)).build(); } @GET @Path("/{id}") public Response getById(@PathParam("id") Long id) { return documentService.findById(id) .map(DocumentResponse::from) .map(Response::ok) .orElse(Response.status(Response.Status.NOT_FOUND)) .build(); } }
java@ApplicationScoped public class DocumentRepository implements PanacheRepository<Document> { public List<Document> findByStatus(DocumentStatus status, int page, int size) { return find("status = ?1 order by createdAt desc", status) .page(page, size) .list(); } public Optional<Document> findByReferenceNumber(String referenceNumber) { return find("referenceNumber", referenceNumber).firstResultOptional(); } public long countByStatusAndDate(DocumentStatus status, LocalDate date) { return count("status = ?1 and createdAt >= ?2", status, date.atStartOfDay()); } }
java@ApplicationScoped @RequiredArgsConstructor public class DocumentService { private final DocumentRepository repo; private final EventService eventService; @Transactional public Document create(CreateDocumentRequest request) { Document document = new Document(); document.setReferenceNumber(request.referenceNumber()); document.setDescription(request.description()); document.setStatus(DocumentStatus.PENDING); document.setCreatedAt(Instant.now()); repo.persist(document); eventService.createSuccessEvent(document, "DOCUMENT_CREATED"); return document; } public Optional<Document> findById(Long id) { return repo.findByIdOptional(id); } public List<Document> list(int page, int size) { return repo.findAll() .page(page, size) .list(); } }
javapublic record CreateDocumentRequest( @NotBlank @Size(max = 200) String referenceNumber, @NotBlank @Size(max = 2000) String description, @NotNull @FutureOrPresent Instant validUntil, @NotEmpty List<@NotBlank String> categories) {} public record DocumentResponse(Long id, String referenceNumber, DocumentStatus status) { public static DocumentResponse from(Document document) { return new DocumentResponse(document.getId(), document.getReferenceNumber(), document.getStatus()); } }
java@Provider public class ValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException> { @Override public Response toResponse(ConstraintViolationException exception) { String message = exception.getConstraintViolations().stream() .map(cv -> cv.getPropertyPath() + ": " + cv.getMessage()) .collect(Collectors.joining(", ")); return Response.status(Response.Status.BAD_REQUEST) .entity(Map.of("error", "validation_error", "message", message)) .build(); } } @Provider @Slf4j public class GenericExceptionMapper implements ExceptionMapper<Exception> { @Override public Response toResponse(Exception exception) { log.error("Unhandled exception", exception); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(Map.of("error", "internal_error", "message", "An unexpected error occurred")) .build(); } }
java@Slf4j @ApplicationScoped @RequiredArgsConstructor public class FileStorageService { private final S3Client s3Client; private final ExecutorService executorService; @ConfigProperty(name = "storage.bucket-name") String bucketName; public CompletableFuture<StoredDocumentInfo> uploadOriginalFile( InputStream inputStream, long size, LogContext logContext, InvoiceFormat format) { return CompletableFuture.supplyAsync(() -> { try (SafeAutoCloseable ignored = CustomLog.startScope(logContext)) { String path = generateStoragePath(format); PutObjectRequest request = PutObjectRequest.builder() .bucket(bucketName) .key(path) .contentLength(size) .build(); s3Client.putObject(request, RequestBody.fromInputStream(inputStream, size)); log.info("File uploaded to S3: {}", path); return new StoredDocumentInfo(path, size, Instant.now()); } catch (Exception e) { log.error("Failed to upload file to S3", e); throw new StorageException("Upload failed", e); } }, executorService); } }
java@ApplicationScoped @RequiredArgsConstructor public class DocumentCacheService { private final DocumentRepository repo; @CacheResult(cacheName = "document-cache") public Optional<Document> getById(@CacheKey Long id) { return repo.findByIdOptional(id); } @CacheInvalidate(cacheName = "document-cache") public void evict(@CacheKey Long id) {} @CacheInvalidateAll(cacheName = "document-cache") public void evictAll() {} }
yaml# application.yml "%dev": quarkus: datasource: jdbc: url: jdbc:postgresql://localhost:5432/dev_db username: dev_user password: ${DB_PASSWORD} hibernate-orm: database: generation: drop-and-create rabbitmq: host: localhost port: 5672 username: ${RABBITMQ_USER} password: ${RABBITMQ_PASSWORD} "%test": quarkus: datasource: jdbc: url: jdbc:h2:mem:test hibernate-orm: database: generation: drop-and-create "%prod": quarkus: datasource: jdbc: url: ${DATABASE_URL} username: ${DB_USER} password: ${DB_PASSWORD} hibernate-orm: database: generation: validate rabbitmq: host: ${RABBITMQ_HOST} port: ${RABBITMQ_PORT} username: ${RABBITMQ_USER} password: ${RABBITMQ_PASSWORD} # Camel configuration camel: rabbitmq: queue: business-rules: business-rules-queue invoice-processing: invoice-processing-queue
java@Readiness @ApplicationScoped @RequiredArgsConstructor public class DatabaseHealthCheck implements HealthCheck { private final AgroalDataSource dataSource; @Override public HealthCheckResponse call() { try (Connection conn = dataSource.getConnection()) { boolean valid = conn.isValid(2); return HealthCheckResponse.named("Database connection") .status(valid) .build(); } catch (SQLException e) { return HealthCheckResponse.down("Database connection"); } } } @Liveness @ApplicationScoped public class CamelHealthCheck implements HealthCheck { @Inject CamelContext camelContext; @Override public HealthCheckResponse call() { boolean isStarted = camelContext.getStatus().isStarted(); return HealthCheckResponse.named("Camel Context") .status(isStarted) .build(); } }
xml<properties> <quarkus.platform.version>3.27.0</quarkus.platform.version> <lombok.version>1.18.42</lombok.version> <assertj-core.version>3.24.2</assertj-core.version> <jacoco-maven-plugin.version>0.8.13</jacoco-maven-plugin.version> <maven.compiler.release>17</maven.compiler.release> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>io.quarkus.platform</groupId> <artifactId>quarkus-bom</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>io.quarkus.platform</groupId> <artifactId>quarkus-camel-bom</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Quarkus Core --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-config-yaml</artifactId> </dependency> <!-- Camel Extensions --> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-spring-rabbitmq</artifactId> </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-direct</artifactId> </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-bean</artifactId> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> <!-- Logging --> <dependency> <groupId>io.quarkiverse.logging.logback</groupId> <artifactId>quarkus-logging-logback</artifactId> </dependency> <dependency> <groupId>net.logstash.logback</groupId> <artifactId>logstash-logback-encoder</artifactId> </dependency> </dependencies>
@RequiredArgsConstructor with Lombok for constructor injectiondirect: endpoints for in-memory routingspring-rabbitmq component for RabbitMQ integrationProducerTemplate.asyncSendBody()SafeAutoCloseable@Slf4j instead of manual logger instantiation.join() when you need to wait for completionquarkus-config-yaml)@ConfigProperty for type-safe config injection@Valid@Provider@Transactional on service methods that modify datacamel-quarkus-junit5 for route testing| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.