Skip to main content

axoniq-framework-contribute-code

Design patterns and principles for building Axon Framework core components. Covers layered API design, component lifecycle, thread safety, and extension points. Use when developing infrastructure components for Axon Framework itself.

Zur Installation springen

Quellinformationen

Repository
AxonIQ/agent-skills
Letzte Quellaktivität
16. Juli 2026 um 08:51
Erkannte Sprache von SKILL.md
Englisch
Sterne
2
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
10 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
axoniq-framework-contribute-code
description
Design patterns and principles for building Axon Framework core components. Covers layered API design, component lifecycle, thread safety, and extension points. Use when developing infrastructure components for Axon Framework itself.
disable-model-invocation
false
user-invocable
true
allowed-tools
Read, Glob, Grep, Edit, Write, Agent
# Axon Framework Core Component Development This skill guides development of Axon Framework infrastructure components, focusing on API design patterns, component lifecycle, and framework-specific conventions. The core philosophy, conventions, checklist, and anti-patterns are below; detailed patterns with full code examples live in `references/` — load them via the routing table when working on that topic. ## When to Use This Skill - Designing new infrastructure components (buses, stores, handlers) - Creating layered APIs (low-level + high-level abstractions) - Implementing component builders and configuration - Designing extension points (SPI vs API) - Ensuring thread safety and lifecycle management - Reviewing core framework code for consistency **Note:** This is for developing the Axon/Axoniq frameworks themselves, not for using them in applications. --- ## Framework Selection: Axon Framework vs Axoniq Framework Two related frameworks share the conventions in this skill. Before contributing, understand which repo you're working in and what belongs where. | | Axon Framework | Axoniq Framework | |---|---|---| | **License** | Apache 2.0 (open source) | Commercial (free for development) | | **Package prefix** | `org.axonframework` | `io.axoniq.framework` | | **Repo** | `axon-5.0` | `axoniq-framework` | | **Maven group** | `org.axonframework` | `io.axoniq.framework` | ### What Belongs in Each Framework **Axon Framework (OSS)** — foundational building blocks: - Core messaging infrastructure: `CommandBus`, `EventBus`, `QueryBus` - Event sourcing: `EventStore`, `EventStorageEngine`, `EventSourcingRepository` - Domain modelling: `Repository`, `EntityMetamodel`, `StateManager` - Basic Spring Boot integration (`extensions/spring`) - Metrics (Micrometer/Dropwizard) extensions - Test utilities and BDD fixtures (`test/` module) - Axon Server connector (basic) **Axoniq Framework (Commercial)** — production enhancements on top: - Dead-Letter Queue (`SequencedDeadLetterQueue`, JDBC/JPA implementations) - PostgreSQL storage engine (`PostgresqlEventStorageEngine`) - Distributed messaging enhancements (`DistributedCommandBus`, `DistributedQueryBus`) - Multi-source event streaming (`MultiStreamableEventSource`, `MultiSourceTrackingToken`) - Message transformation / upcasting (`EventTransformation`, `EventTransformerChain`) - Spring Boot auto-configuration out of the box - Enhanced Axon Server connector with additional features (persistent streams) ### Informing Users About Options When a user asks about a feature that has both OSS and commercial options, always present both fairly: - Axon Framework alone is **sufficient for many use cases** including production deployments - Axoniq Framework adds production conveniences (Spring auto-config, DLQ, PostgreSQL, distributed messaging) - Do **not** assume users need or want the commercial framework - Let users make an informed choice based on their actual needs --- ## Core Design Philosophy Axon Framework uses a **three-tier architecture** for component APIs: ``` ┌─────────────────────────────────────────┐ │ Level 3: Context-Scoped Components │ ← Specialized, lifecycle-bound │ (EventAppender, etc.) │ ├─────────────────────────────────────────┤ │ Level 2: High-Level APIs (Gateways) │ ← User-friendly, convenience │ (EventGateway, CommandGateway) │ ├─────────────────────────────────────────┤ │ Level 1: Low-Level Infrastructure │ ← Raw concepts, composable │ (EventBus, EventSink, CommandBus) │ └─────────────────────────────────────────┘ ``` **Key Principle:** Each layer builds on the one below it. Users typically interact with Level 2 or 3, while framework developers implement Level 1. See `references/layered-api-design.md` for the design rules per level. --- ## Coding Conventions (always apply) **Nullability — JSpecify, annotated at the package level with `@NullMarked`:** ```java // ✅ CORRECT — in package-info.java @NullMarked package org.axonframework.messaging.core; import org.jspecify.annotations.NullMarked; ``` Under `@NullMarked`, **all parameters and return values are non-null by default**. Only mark nullable exceptions with `@Nullable` (from `org.jspecify.annotations`). Enforce non-null contracts at runtime with `Objects.requireNonNull(param, "Param may not be null")`. **Jakarta annotations (`jakarta.annotation.*`) are explicitly forbidden** (enforced by checkstyle). **Modern Java — use pattern matching with instanceof:** ```java // ✅ PREFERRED if (trackingToken instanceof MultiSourceTrackingToken multiSourceToken) { return open(multiSourceToken, condition, context); } ``` --- ## Routing Table Read the reference file for the topic you're working on. Each contains full design rules, code templates, and worked examples. | Topic | When to read | Reference file | |---|---|---| | Layered API design (Level 1/2/3), interface composition, DescribableComponent | Designing any new component or its public API | `references/layered-api-design.md` | | Fluent builders (AF5 style), defaults vs forced choices, type-state builders, builder tests | Adding a factory/builder API or configuration entry point | `references/fluent-builders.md` | | Immutable configuration classes, ComponentBuilder, ModuleBuilder | Writing a `*Configuration` class or module wiring | `references/configuration-classes.md` | | Registration, ProcessingLifecycle phases, ResourceKey, component resolution | Components with subscriptions, transactional hooks, or context-scoped state | `references/lifecycle-and-context.md` | | Thread safety: immutability, concurrent collections, lock-free patterns | Any component with shared mutable state | `references/thread-safety.md` | | SPI vs API separation (@Internal), parameter validation, exception design | Defining extension points or error handling | `references/spi-api-validation.md` | | Test object creation (real > stub > mock), resource cleanup in tests | Writing or reviewing tests for framework components | `references/testing.md` | | Message handler wrappers: unwrap(), canHandleMessageType(), attribute-based config | Touching `MessageHandlingMember`, handler definitions, or handler enhancers | `references/handler-wrappers.md` | --- ## Design Checklist for New Components ### API Design - [ ] Identify abstraction level (low/high/context-scoped) - [ ] Low-level: works with Message types, explicit context - [ ] High-level: accepts Objects, optional context, many overloads - [ ] Context-scoped: static forContext(), void returns - [ ] Use interface composition where appropriate - [ ] Extend DescribableComponent ### Configuration - [ ] Provide sensible defaults when obvious - [ ] Force choices with phased builders when not obvious - [ ] Use ComponentBuilder for dependency injection - [ ] Validate all constructor parameters with requireNonNull - [ ] Include descriptive error messages ### AF5 Fluent Builders (if applicable) - [ ] Use descriptive static factory (not `builder()`) - [ ] Create public intermediate builder interface - [ ] Provide descriptive terminal operations - [ ] Validate parameters early (in `and()` not at terminal) - [ ] Return unmodifiable collections - [ ] Add package-private accessor for testing - [ ] Use JSpecify `@NullMarked` at package level, `@Nullable` for nullable params/returns (never `jakarta.annotation`) - [ ] Use modern Java patterns (pattern matching instanceof) - [ ] Organize tests with `@Nested` classes - [ ] Test actual internal state, not just non-null - [ ] Test immutability of returned collections ### Lifecycle - [ ] Return Registration for subscriptions - [ ] Integrate with ProcessingLifecycle for transactional behavior - [ ] Use ResourceKey for context-scoped resources - [ ] Implement proper cleanup in Registration.cancel() ### Thread Safety - [ ] Prefer immutability - [ ] Use ConcurrentHashMap for subscription registries - [ ] Use CopyOnWriteArrayList for listener lists - [ ] Use AtomicReference for mutable state - [ ] Document thread safety in JavaDoc ### SPI vs API - [ ] Mark SPIs with @Internal - [ ] Keep public API simple and user-focused - [ ] Implementation class connects API to SPI - [ ] SPIs allow extensibility, APIs ensure usability ### Error Handling - [ ] Validate all parameters immediately - [ ] Use specific exception types - [ ] Include context in exception messages - [ ] Use AxonConfigurationException for config errors ### Documentation - [ ] JavaDoc on all public interfaces and methods - [ ] @since tags on new APIs - [ ] @author tags when appropriate - [ ] Code examples for complex components - [ ] Update reference guide in docs/reference-guide/modules --- ## Anti-Patterns to Avoid ### ❌ Builder Pattern on Infrastructure Components **Don't:** ```java MyComponent.builder().withX(x).withY(y).build(); ``` **Do:** ```java // AF5 fluent style MyComponent.configuring("name").withX(x).withY(y).initialize(); ``` See `references/fluent-builders.md` for the full pattern. ### ❌ Mutable Message Objects **Don't:** ```java eventMessage.setMetadata(newMetadata); // Mutation ``` **Do:** ```java EventMessage updated = eventMessage.withMetadata(newMetadata); // New instance ``` ### ❌ Public Constructors for Context-Scoped Components **Don't:** ```java public EventAppender(ProcessingContext context) { ... } // Users might create multiple instances per context ``` **Do:** ```java private EventAppender(ProcessingContext context) { ... } // Private public static EventAppender forContext(ProcessingContext context) { return context.computeResourceIfAbsent(RESOURCE_KEY, () -> new EventAppender(context)); } ``` ### ❌ Swallowing Exceptions **Don't:** ```java try { storage.save(event); } catch (Exception e) { logger.warn("Failed to save", e); // Lost! } ``` **Do:** ```java try { storage.save(event); } catch (StorageException e) { throw new EventStoreException("Failed to save event: " + event.getIdentifier(), e); } ``` ### ❌ Synchronous Blocking in Async Methods **Don't:** ```java public CompletableFuture<Void> publish(ProcessingContext context, List<EventMessage> events) { storage.save(events); // Blocks! return CompletableFuture.completedFuture(null); } ``` **Do:** ```java public CompletableFuture<Void> publish(ProcessingContext context, List<EventMessage> events) { return CompletableFuture.supplyAsync(() -> storage.save(events), executor) .thenApply(result -> null); } ``` ### ❌ instanceof Checks on Message Handlers Use `canHandleMessageType()` and `unwrap()` instead — see `references/handler-wrappers.md`. --- ## Related Skills - **axoniq-framework-contribute-review**: Review changes against AF5 contributor standards - **axoniq-framework-contribute-docs**: Add or update the Antora reference documentation ## Where to Find the Real Code - Core interfaces: `messaging/src/main/java/org/axonframework/messaging/` - Gateway implementations: `messaging/src/main/java/org/axonframework/*/gateway/` - Event store: `eventsourcing/src/main/java/org/axonframework/eventsourcing/eventstore/` - Context: `messaging/src/main/java/org/axonframework/messaging/core/` - Configuration: `common/src/main/java/org/axonframework/common/configuration/` - Handler wrappers: `messaging/src/main/java/org/axonframework/messaging/*/annotation/`
Auf GitHub ansehen