簡體   English   中英

事件類隨 Axon CQRS 發生變化

[英]Event class changes with Axon CQRS

在 axon 中測試事件遷移時,我發現具有默認值的新字段實際上設置為 null。 將字段(具有默認值)實際添加到先前事件的好方法是什么? 我不希望 MyEventV2 的值是強制性的(沒有默認值)並且不想指定“upcaster”。

首先,我們有這個事件,它被發布並存儲在 axon 服務器中:

data class MyEvent(
    val someId: String,
    val name: String
)

稍后我們更改 MyEvent,我們給出一個默認值以保持與之前事件的兼容性持久化,我們希望舊事件具有此默認值。 真正的結果是:'newField' 在接收舊事件時為空。

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

我測試了 axon 服務器,axon 4.2,spring boot 2.2.0,axon boot starter 的所有默認值,Jackson 2.10.0

我們應該使用類似的東西(但根本不是很好......):

data class MyEvent(
    val someId: String,
    val name: String,
    private val newFieldV2: String? = null
) {
    val newField: String
        get() = newFieldV2 ?: "my default value"
}

我還沒有檢查 axon 如何從事件存儲中重建事件,但我猜它是通過字段訪問(反射)而不是構造函數來完成的。 但是很奇怪,因為數據類沒有提供默認的空構造函數......

編輯:Axon 默認使用 XStreamSerializer,我已將配置切換為使用 Jackson 但問題仍然存在,並且 Jackson kotlin 模塊已注冊。

EDIT2:更新到 Jackson 2.10.1(見 問題),它應該修復但由於某種原因我得到另一個錯誤“com.fasterxml.jackson.databind.exc.InvalidDefinitionException:無法構造org.xxMyEvent實例(沒有創建者,如默認構造,存在):不能從對象值反序列化(沒有基於委托或屬性的創建者)”。

如果添加“ParameterNamesModule”,則會出現錯誤,如果未添加,則事件的默認值仍然為空。

EDIT3:我寫了測試,但 axon 仍然有事件 org.axonframework.serialization.json.JacksonSerializer 的錯誤

@ExperimentalStdlibApi
class JsonDefaultValueTest {

    private lateinit var mapper1: ObjectMapper
    private lateinit var mapper2: ObjectMapper
    private lateinit var mapper3: ObjectMapper
    private lateinit var mapper4: ObjectMapper
    private lateinit var mapper5: ObjectMapper
    private lateinit var mapper6: ObjectMapper

    @BeforeEach
    fun setup() {
        mapper1 = Jackson2ObjectMapperBuilder.json()
            .build()
        mapper2 = ObjectMapper()
        mapper3 = ObjectMapper().registerModule(KotlinModule())
        mapper4 = ObjectMapper().registerModule(KotlinModule(nullisSameAsDefault = true))
        mapper5 = ObjectMapper().registerModule(ParameterNamesModule())
        mapper6 = ObjectMapper()
            .registerModule(ParameterNamesModule())
            .registerModule(KotlinModule())
            .registerModule(Jdk8Module())
            .registerModule(JavaTimeModule())
    }

    @Test
    fun `only s1 passed with mapper 1`() {
        val json = """{"s1":"only s1"}"""

        val event = mapper1.readValue<MyEvent>(json)

        val event2 = mapper1.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray())

        val expected = MyEvent(
            "only s1",
            aInt = 0
        )

        Assertions.assertEquals(expected, event)
    }

    @Test
    fun `only s1 passed with mapper 2`() {
        val json = """{"s1":"only s1"}"""

        assertThrows<InvalidDefinitionException> { mapper2.readValue<MyEvent>(json) }

        assertThrows<InvalidDefinitionException> { mapper2.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray()) }

    }

    @Test
    fun `only s1 passed with mapper 3`() {
        val json = """{"s1":"only s1"}"""

        val event = mapper3.readValue<MyEvent>(json)

        val event2 = mapper3.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray())

        val expected = MyEvent(
            "only s1",
            aInt = 0
        )

        Assertions.assertEquals(expected, event)
        Assertions.assertEquals(expected, event2)
    }

    @Test
    fun `only s1 passed with mapper 4`() {
        val json = """{"s1":"only s1"}"""

        val event = mapper4.readValue<MyEvent>(json)

        val event2 = mapper4.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray())

        val expected = MyEvent(
            "only s1",
            aInt = 0
        )

        Assertions.assertEquals(expected, event)
        Assertions.assertEquals(expected, event2)
    }

    @Test
    fun `only s1 passed with mapper 5`() {
        val json = """{"s1":"only s1","s2":null}"""

        assertThrows<ValueInstantiationException> { mapper5.readValue<MyEvent>(json) }

        assertThrows<ValueInstantiationException> { mapper5.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray()) }

    }

    @Test
    fun `only s1 passed with mapper 6`() {
        val json = """{"s1":"only s1"}"""

        val event = mapper6.readValue<MyEvent>(json)

        val event2 = mapper6.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray())

        val expected = MyEvent(
            "only s1",
            aInt = 0
        )

        Assertions.assertEquals(expected, event)
        Assertions.assertEquals(expected, event2)
    }

    data class MyEvent(
        val s1: String,
        val aInt: Int,
        val s2: String = "my default"
    )
}

這似乎是 Jackson-Kotlin 問題。 您需要明確配置 Jackson 以將“null”(或缺少字段)視為“使用默認值”。

正如https://github.com/FasterXML/jackson-module-kotlin/issues/130#issuecomment-546625625 中推薦的那樣,您應該使用:

ObjectMapper().registerModule(KotlinModule(nullisSameAsDefault = true)

這聽起來像是用於反序列化事件的 Jackson ObjectMapper 的錯誤配置。 為了使其與 Kotlin 特定的功能(例如默認值)一起使用,您需要注冊 Kotlin 模塊: https : //github.com/FasterXML/jackson-module-kotlin

val mapper = ObjectMapper().registerModule(KotlinModule())

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM