Why in Axon 5 is message metadata now Map<String, String> rather than Map<String, Object>

org.axonframework.messaging.MetaData
and
org.axonframework.commandhandling.GenericCommandMessage

both now define MetaData as Map<String, String>

What’s the rational behind this decision in Axon 5, won’t any serialisable type work? I want to store auth roles in metadata as List<String>

I am currently doing that in Axon 5 with this approach and wanted to be able to return Map<String, Any> from my claimsExtractor.currentMetaData() to avoid repeating the same deserialisation or String.split code within all my command handler methods.

I know it’s not exactly the end of the world just curious, will this be in the final GA release of Axon 5? Is this to facilitate some incoming metadata based routing feature or something


package uk.specit.security

import org.axonframework.commandhandling.GenericCommandMessage
import org.axonframework.commandhandling.gateway.CommandResult
import org.axonframework.messaging.MessageType
import org.axonframework.commandhandling.gateway.CommandGateway as OriginalCommandGateway
import org.springframework.stereotype.Component

@Component
class AuthenticatedCommandGateway(
    private val originalCommandGateway: OriginalCommandGateway,
    private val claimsExtractor: AuthClaimsExtractor
) {
    /**
     * Sends the given command, enriching it with authentication metadata derived from the
     * current Spring Security context (Microsoft Entra OIDC). This avoids relying on
     * deprecated/removed gateway interceptors in Axon 5 milestones by explicitly attaching
     * minimal trusted claims as command metadata.
     */
    fun send(command: Any): CommandResult = originalCommandGateway.send(
        GenericCommandMessage(
            MessageType(
                command::class.qualifiedName.toString()
            ),
            command,
            claimsExtractor.currentMetaData()
        ),
        null
    )
}

The conversion of MetaData with all its flexibility due to allowing the value to be any Object, has given the Axon Framework team many issues over the years. As part of Axon Framework 5, we are greatly simplifying the serialization/conversion flow of everything. And as part of that endeavor, we have decided to enforce the value of MetaData to a String.

Our reasoning is that 9 out of 10, the stored value is not a complex type. If it is a complex type, mapping is either (1) straightforward or (2) requires come conversion. For option 1, we expect users to build a solution in place. For option 2, you could use Axon Framework 5’s Converter (read: the replacement of the Serializer) interface. You can inject the Converter automatically in any message handling or intercepting component, convert the complex object to String and store it in the MetaData. When reading, you’d again inject the Converter to do the conversion. I think that given you have a List<String>, you could deconstruct it into a comma-separated and concatenated String and store the value.

I acknowledge that this shift is taking away a feature that used to exist in Axon Framework 4. We have discussed about it quite extensively, but feel that the benefits it provides (e.g. a clear expectation of what will be passed) outway the flexibility. Added, with the relatively easy workarounds, we figured this is the right decision.

By the way, we are thinking of allowing the @MetaDataValue annotation, which you can place on message handler parameters to automatically inject MetaData values, to perform a conversion for the user.

Concluding, I hope that clarifies our angle, @david! And, feel free to give your opinion on this shift. :pray:

1 Like