Version 0.0.15 is the Quarkus integration release — and a lot more. It ships first-class Quarkus transaction support with full JTA propagation, five new integration modules that extend dmx-fun to HTTP, serialization, and observability, a published Bill of Materials, CycloneDX SBOMs for every artifact, and a set of correctness fixes that close subtle edge cases found through expanded test coverage.

Here is everything that changed.


fun-quarkus — first-class Quarkus transaction support

Quarkus’s @Transactional (backed by Narayana JTA) rolls back only when an unchecked exception escapes the annotated method. Result and Try capture failure as return values — so no exception escapes, and the transaction commits even when the operation failed, silently persisting partial writes.

fun-quarkus fixes this with two complementary styles.

Early-stage support: The fun-quarkus module is in its early stages. The API is functional and tested, but you may encounter rough edges in real-world usage. Feedback and bug reports are very welcome on GitHub.

Programmatic: TxResult and TxTry

@ApplicationScoped
public class OrderService {
@Inject TxResult tx;
@Inject OrderRepository repo;
public Result<Order, String> place(PlaceOrderCommand cmd) {
return tx.execute(() -> {
if (cmd.quantity() <= 0) return Result.err("quantity must be positive");
Order order = repo.save(new Order(cmd));
return Result.ok(order);
});
}
}

TxResult.execute commits when the result is Ok and rolls back when it is Err. TxTry follows the same contract for Try<V>.

Both also provide executeNew() for REQUIRES_NEW semantics — the active transaction is suspended, an independent one starts, and the original resumes after the action finishes regardless of the outcome:

// Audit log must commit even if the outer business transaction rolls back
Result<AuditEntry, String> audit = auditTx.executeNew(() ->
auditRepo.record(event)
);

Declarative: @TransactionalResult and @TransactionalTry

@ApplicationScoped
public class InventoryService {
@TransactionalResult
public Result<Item, String> reserve(long itemId, int qty) {
return repo.findById(itemId)
.map(item -> item.reserve(qty))
.toResult(() -> "item not found");
// Err return → rollback; unchecked exception → rollback + rethrow
}
}

Both annotations accept a Transactional.TxType attribute (default REQUIRED), covering all six JTA propagation types:

TxTypeExisting transaction presentNo active transaction
REQUIRED (default)Joins; marks rollback-only on errorBegins a new transaction
REQUIRES_NEWSuspends outer, starts fresh, resumes outer afterStarts a fresh transaction
MANDATORYJoins; marks rollback-only on errorThrows TransactionalException
SUPPORTSJoins; marks rollback-only on errorRuns without a transaction
NOT_SUPPORTEDSuspends outer, runs without one, resumes outer afterRuns without a transaction
NEVERThrows TransactionalExceptionRuns without a transaction
@ApplicationScoped
public class AuditService {
@TransactionalResult(REQUIRES_NEW)
public Result<AuditEntry, String> record(AuditEvent event) { ... }
@TransactionalResult(NOT_SUPPORTED)
public Result<Report, String> generateReport(ReportRequest req) { ... }
@TransactionalResult(MANDATORY)
public Result<Void, String> postProcess(Order order) { ... }
}

Both annotation types route to a single interceptor class (TransactionalDmxInterceptor) via the shared @DmxTransactionalBinding meta-annotation (CDI §2.7.1.1), so there is no runtime overhead of maintaining two separate interceptor registrations.

quarkus-arc and quarkus-narayana-jta are declared compileOnly; your own Quarkus version is not forced. The module ships META-INF/quarkus-extension.properties so Quarkus build tools automatically add fun-quarkus-deployment to the augmentation classpath — no explicit deployment dependency needed in your project.

Tested against Quarkus 3.11.3, 3.21.4, 3.31.4, and 3.35.1 in CI via a compatibility matrix. Integration tests run against a real PostgreSQL instance through Quarkus Dev Services (Testcontainers).

// Gradle (Kotlin DSL)
implementation("codes.domix:fun-quarkus:0.0.15")

fun-http — HttpClient wrapper returning Result

fun-http wraps the JDK java.net.http.HttpClient and returns Result<T, HttpError> instead of throwing. All HTTP and network failures become typed Err values with no unchecked exception leaking into your application code.

HttpError is a sealed type with variants for network failures, non-2xx responses, and deserialization errors, making exhaustive handling straightforward with switch or fold.

implementation("codes.domix:fun-http:0.0.15")

fun-jakarta-jaxb — Jakarta JSON-B adapters

fun-jakarta-jaxb provides Jakarta JSON-B adapters for Option, Result, Try, Either, and Validated. Register them once with JsonbConfig for full round-trip serialization support across all dmx-fun types.

Jakarta JSON-B 3.x is declared compileOnly.

implementation("codes.domix:fun-jakarta-jaxb:0.0.15")

fun-jakarta-validation — Jakarta Validation integration

fun-jakarta-validation integrates Jakarta Validation 3.x / 4.x constraint violations with Validated<E, A> for accumulating validation errors. Constraint violations are lifted into the Validated error channel instead of being collected through ConstraintViolationException.

Jakarta Validation 3.x is declared compileOnly.

implementation("codes.domix:fun-jakarta-validation:0.0.15")

fun-tracing — Micrometer Tracing integration

fun-tracing wraps a Micrometer Tracer and creates spans around Try- and Result-returning suppliers. Failure outcomes are automatically tagged on the active span without any manual instrumentation.

When fun-spring-boot is on the classpath and Micrometer Tracing is present, a DmxTracing bean is auto-registered — zero configuration required.

Micrometer Tracing is declared compileOnly.

implementation("codes.domix:fun-tracing:0.0.15")

fun-observation — Micrometer Observation API integration

fun-observation wraps a Micrometer ObservationRegistry and records observations around Try- and Result-returning suppliers. Failure outcomes are tagged on the active observation.

Micrometer Observation is declared compileOnly.

implementation("codes.domix:fun-observation:0.0.15")

fun-bom — Bill of Materials

fun-bom is now published to Maven Central. Import it in dependencyManagement to align all dmx.fun module versions without specifying each individually:

// Gradle (Kotlin DSL)
implementation(platform("codes.domix:fun-bom:0.0.15"))
implementation("codes.domix:fun") // no version needed
implementation("codes.domix:fun-quarkus") // no version needed
testImplementation("codes.domix:fun-assertj")
<!-- Maven -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>codes.domix</groupId>
<artifactId>fun-bom</artifactId>
<version>0.0.15</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

CycloneDX SBOMs

Every published module now ships a *-cyclonedx.json Software Bill of Materials alongside the JAR and sources artifact on Maven Central. SBOMs are available for all modules starting with this release.


Bug fixes

fun-quarkus — STATUS_MARKED_ROLLBACK treated as active transaction

hasActiveTransaction() previously only matched STATUS_ACTIVE. After setRollbackOnly() is called, the JTA status transitions to STATUS_MARKED_ROLLBACK — a state where the transaction is still active and joinable but has been marked for rollback. The old check caused REQUIRED to start a new transaction instead of joining, MANDATORY to throw, and NEVER to silently succeed when the enclosing transaction had already been marked rollback-only. Both statuses are now treated as an active, joinable transaction.

fun-quarkus — Error and infrastructure failures in executeJoined

executeJoined previously only caught RuntimeException. JVM errors such as AssertionError or OutOfMemoryError would escape without marking the joined transaction rollback-only. The handler now catches RuntimeException | Error.

Additionally, setRollbackOnly() infrastructure failures are no longer silently swallowed. When a setRollbackOnly() failure coincides with an application exception, the infrastructure exception is attached as a suppressed exception so neither failure is lost.

fun-micrometer / fun-tracing / fun-observation — high-cardinality exception tag eliminated

The default exception tag used getClass().getSimpleName(), producing an unbounded number of tag values when arbitrary third-party exceptions appear at runtime — a violation of Micrometer’s low-cardinality contract. A configurable exceptionClassifier function now maps each exception to a bounded label; the built-in default maps all Throwable subclasses to "exception" unless overridden.

fun-jackson — null exception message and Tuple2 null elements

Failure serialization now handles a null exception message without throwing NPE. Tuple2 deserializer guards against null first and second elements.

fun-assertj — angle brackets preserved in error messages

Generic type names such as Result<V, E> were stripped of angle brackets in assertion failure messages. The formatter now escapes them correctly.


Other changes

  • fun-springTxResult, TxTry, and TxValidated accept PROPAGATION_REQUIRES_NEW and readOnly flags; declarative annotations honor Spring’s @Transactional propagation contract in all edge cases.
  • fun-resilience4j — per-artifact version strings replaced with a platform("io.github.resilience4j:resilience4j-bom") dependency for consistent version management.
  • Repository structure reorganized into logical groups: core/, serialization/, observability/, frameworks/, protocols/, samples/.
  • Spring Boot upgraded to 4.0.6.
  • Gradle wrapper upgraded to 9.5.0.

Getting the release

// Gradle (Kotlin DSL) — use the BOM to manage all versions
implementation(platform("codes.domix:fun-bom:0.0.15"))
implementation("codes.domix:fun")
implementation("codes.domix:fun-quarkus") // Quarkus transaction support
implementation("codes.domix:fun-http") // HttpClient wrapper
implementation("codes.domix:fun-jakarta-jaxb") // Jakarta JSON-B adapters
implementation("codes.domix:fun-tracing") // Micrometer Tracing
implementation("codes.domix:fun-observation") // Micrometer Observation
testImplementation("codes.domix:fun-assertj")
<!-- Maven — import the BOM first -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>codes.domix</groupId>
<artifactId>fun-bom</artifactId>
<version>0.0.15</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>codes.domix</groupId>
<artifactId>fun</artifactId>
</dependency>
<dependency>
<groupId>codes.domix</groupId>
<artifactId>fun-quarkus</artifactId>
</dependency>
</dependencies>

See the full changelog for the complete list of changes, fixes, and build improvements in this release.


Found a bug or have a suggestion? Open an issue on GitHub.