Event class evolution

While testing event migration in axon I found out that new fields with a default value are set to null in fact.
What would be the good to actually add a field (with default value) to previous events?
I do not want to have a MyEventV2 where the value is mandatory (no default value) and do not want to specify a upcaster.

Let’s we have first this event and it was published and stored in axon server:
data class MyEvent(
val someId: String,
val name: String
)

Later we change MyEvent, we give a default value to keep compatibility with previous events persisted, we expect old events to have this default value.
The real result is ‘newField’ is null when receive old events.

data class MyEvent(
val someId: String,
val name: String,
val newField: String = “defaultValueButWillBeNullInPreviousEvents…->would need it to have the actual default value”
)

I tested with axon server, axon 4.2, spring boot 2.2.0, all defaults from axon boot starter, jackson 2.10.0

Hello Yoann,

serializers behave slightly differently when it comes to default values. Did you explicitly configure the Jackson based serializer? The default is the XStreamSerializer, which uses “tricks” to instantiate instances of classes in certain situations. If you have a default constructor on your object, XStream will use that, and default values will be properly in place. If you don’t have a default constructor, you’re likely to get null values instead.

Note that this is more configuration of your serialization framework/library than Axon. Check the serialized form of your events and make sure your serializers create objects in the form you expect.

Hope this helps.
Cheers,

Thanks for the explanation.
I created the issue on: https://stackoverflow.com/questions/59191591/event-class-changes-with-axon-cqrs

I configured axon to use jackson (before was default XStreamSerializer) but still have the issue.

Thanks

I’m not certain this will address the issue (it depends on the “tricks” used), but I think that if you overload the constructors in order to provide one that does not include the newField attribute, but provides it with a default value, it may address your issue. Adding @JvmOverloads constructor to your data class definition will do that for you.

`
data class MyEvent @JvmOverloads constructor(
val someId: String,
val name: String,
val newField: String = “SomeDefaultValue”
)

`

Thanks, but did not work.
I have configured modules KotlinModule (and also tried with KotlinModule(nullisSameAsDefault = true)), and ParameterNamesModule and JavaTimeModule and Jdk8Module for the ObjectMapper and set it for the JacksonSerializer of Axon

I even wrote some tests to make sure it should work.
There is an issue with how JacksonSerializer is working or I missconfigured the ObjectMapper eventhough I did configure it manually for JacksonSerializer

Hi Yoann,

this feels more like a Jackson issue than an Axon one, at the moment.
Can you confirm it works when you use the ObjectMapper directly to deserialize the object?

Cheers,

Hi,

Yes it works if I instantiate the ObjectMapper directly in my test class but it does not work when I define it and let Spring boot auto configuration (and axon auto conf) use the ObjectMapper bean I defined manually.
I checked in debug mode and my ObjectMapper is used for JacksonSerializer of Axon but issue still there.

I put my tests examples in:

https://stackoverflow.com/questions/59191591/event-class-changes-with-axon-cqrs

Hi Yoann,

I’ve just adapted my Bike Rental demo application to serialize using Jackson, and is works fine with default parameter values in Kotlin. I just leave all the configuration to Spring boot and Axon. I have not specified anything in my application contexts.
Here is my relevant config:

org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE

org.axonframework axon-spring-boot-starter 4.2 com.fasterxml.jackson.module jackson-module-kotlin

In application.properties:
axon.serializer.messages=jackson

Cheers,