Source profileQuality 85/100

affaan-m/ECC/skills/quarkus-patterns/SKILL.md

quarkus-patterns

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.

Source repository stars
234,327
Declared platforms
0
Static risk flags
0
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Quarkus 3.x architecture and API patterns for cloud-native, event-driven services with Apache Camel.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "skills/quarkus-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "quarkus-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/quarkus-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      When to Activate

      Building REST APIs with JAX-RS or RESTEasy Reactive

      Building REST APIs with JAX-RS or RESTEasy ReactiveStructuring resource → service → repository layersImplementing event-driven patterns with Apache Camel and RabbitMQ
    2. 02

      Service Layer with Multiple Dependencies

      Key Patterns: - @RequiredArgsConstructor for constructor injection via Lombok - @Slf4j for Logback logging - @Transactional on service methods that write through Panache or repositories - Validate input before persistence or message publication - Event tracking for success/error…

      @RequiredArgsConstructor for constructor injection via Lombok@Slf4j for Logback logging@Transactional on service methods that write through Panache or repositories
    3. 03

      Custom Logging Context Pattern (Logback)

      Logback Configuration (logback.xml):

      Logback Configuration (logback.xml):
    4. 04

      Event Service Pattern

      Review the “Event Service Pattern” section in the pinned source before continuing.

      Review and apply the “Event Service Pattern” source section.
    5. 05

      Camel Message Publishing (RabbitMQ)

      Camel Route Configuration:

      Camel Route Configuration:

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score85/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    skills/quarkus-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Quarkus Development Patterns

    Quarkus 3.x architecture and API patterns for cloud-native, event-driven services with Apache Camel.

    When to Activate

    • Building REST APIs with JAX-RS or RESTEasy Reactive
    • Structuring resource → service → repository layers
    • Implementing event-driven patterns with Apache Camel and RabbitMQ
    • Configuring Hibernate Panache, caching, or reactive streams
    • Adding validation, exception mapping, or pagination
    • Setting up profiles for dev/staging/production environments (YAML config)
    • Custom logging with LogContext and Logback/Logstash encoder
    • Working with CompletableFuture for async operations
    • Implementing conditional flow processing
    • Working with GraalVM native compilation

    Service Layer with Multiple Dependencies

    @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 repositories
    • Validate input before persistence or message publication
    • Event tracking for success/error scenarios
    • Async Camel message publishing

    Custom Logging Context Pattern (Logback)

    @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):

    <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>
    

    Event Service Pattern

    @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);
            }
        }
    }
    

    Camel Message Publishing (RabbitMQ)

    @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:

    @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);
        }
    }
    

    Camel Direct Routes (In-Memory)

    @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");
        }
    }
    

    Camel File Processing

    @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");
        }
    }
    

    Camel Bean Invocation

    @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')");
        }
    }
    

    REST API Structure

    @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();
      }
    }
    

    Repository Pattern (Panache Repository)

    @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());
      }
    }
    

    Service Layer with Transactions

    @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();
      }
    }
    

    DTOs and Validation

    public 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());
      }
    }
    

    Exception Mapping

    @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();
      }
    }
    

    CompletableFuture Async Operations

    @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);
        }
    }
    

    Caching

    @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() {}
    }
    

    Configuration as 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
    

    Health Checks

    @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();
      }
    }
    

    Dependencies (Maven)

    <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>
    

    Best Practices

    Architecture

    • Use @RequiredArgsConstructor with Lombok for constructor injection
    • Keep service layer thin; delegate complex logic to specialized classes
    • Use Camel routes for message routing and integration patterns
    • Prefer Panache Repository pattern for data access

    Event-Driven

    • Always track operations with EventService (success/error events)
    • Use Camel direct: endpoints for in-memory routing
    • Use spring-rabbitmq component for RabbitMQ integration
    • Implement async publishing with ProducerTemplate.asyncSendBody()

    Logging

    • Use Logback with Logstash encoder for structured logging
    • Propagate LogContext through service calls with SafeAutoCloseable
    • Add contextual information to LogContext for request tracing
    • Use @Slf4j instead of manual logger instantiation

    Async Operations

    • Use CompletableFuture for non-blocking I/O operations
    • Call .join() when you need to wait for completion
    • Handle exceptions from CompletableFuture properly
    • Pass LogContext to async operations for tracing

    Configuration

    • Use YAML configuration (quarkus-config-yaml)
    • Profile-aware configuration for dev/test/prod environments
    • Externalize sensitive configuration to environment variables
    • Use @ConfigProperty for type-safe config injection

    Validation

    • Validate at resource layer with @Valid
    • Use Bean Validation annotations on DTOs
    • Map exceptions to proper HTTP responses with @Provider

    Transactions

    • Use @Transactional on service methods that modify data
    • Keep transactions short and focused
    • Avoid calling async operations within transactions

    Testing

    • Use camel-quarkus-junit5 for route testing
    • Use AssertJ for assertions
    • Mock all external dependencies
    • Test conditional flow logic thoroughly

    Quarkus-Specific

    • Stay on latest LTS version (3.x)
    • Use Quarkus dev mode for hot reload
    • Add health checks for production readiness
    • Test native compilation compatibility periodically

    Alternatives

    Compare before choosing