$30 off During Our Annual Pro Sale. View Details »

Dealing with change in event sourced applications (ConFoo 2023)

Dealing with change in event sourced applications (ConFoo 2023)

In software development, change is pretty much the only constant factor. In fact, embracing change is one of the twelve principles behind the Agile Manifesto. However, changing event sourced applications can be very challenging.

In this talk, we’ll explore how to deal with projection updates, event updates and versioning, and existing privacy legislation (such as the GDPR).

Michiel Rook

February 24, 2023
Tweet

More Decks by Michiel Rook

Other Decks in Programming

Transcript

  1. DEALING WITH CHANGE

    IN EVENT SOURCED
    APPLICATIONS
    Michiel Rook

    @michieltcs

    View Slide

  2. @michieltcs
    #StandWithUkraine
    YOU?

    View Slide

  3. @michieltcs
    #StandWithUkraine
    FAMILIAR WITH
    CQRS/ES THEORY?

    View Slide

  4. @michieltcs
    #StandWithUkraine
    HOBBY PROJECT?

    View Slide

  5. @michieltcs
    #StandWithUkraine
    APP IN
    PRODUCTION?

    View Slide

  6. @michieltcs
    #StandWithUkraine
    QUICK RECAP

    View Slide

  7. @michieltcs
    #StandWithUkraine
    - M A RT I N FOW L E R
    "Event Sourcing ensures that all
    changes to application state are stored
    as a sequence of events."

    View Slide

  8. @michieltcs
    #StandWithUkraine
    ACTIVE RECORD VS. EVENT SOURCING
    Account Id


    Account number Balance
    1234 12345678 € 50,00
    ... ... ...
    Money Withdrawn
    Account Id 1234
    Amount € 50,00
    Money Deposited
    Account Id 1234
    Amount € 100,00
    Account Opened
    Account Id 1234
    Account number 12345678

    View Slide

  9. @michieltcs
    #StandWithUkraine
    COMMANDS TO EVENTS
    Deposit Money
    Account Id 1234
    Amount € 100,00
    data class DepositMoney(
    @TargetAggregateIdentifier val accountId: String,
    val amount: BigDecimal
    )

    View Slide

  10. @michieltcs
    #StandWithUkraine
    COMMANDS TO EVENTS
    Deposit Money
    Account Id 1234
    Amount € 100,00
    command

    handler
    @CommandHandler
    fun depositMoney(command: DepositMoney) {
    apply(MoneyDeposited(
    command.accountId,
    command.amount,
    GenericEventMessage.clock.instant()))
    }

    View Slide

  11. @michieltcs
    #StandWithUkraine
    COMMANDS TO EVENTS
    Deposit Money
    Account Id 1234
    Amount € 100,00
    Money Deposited
    Account Id 1234
    Amount € 100,00
    command

    handler
    data class MoneyDeposited(
    val accountId: String,
    val amount: BigDecimal,
    val instant: Instant
    )

    View Slide

  12. @michieltcs
    #StandWithUkraine
    AGGREGATES
    An Aggregate handles
    Commands and
    generates Events
    based on the current
    state

    View Slide

  13. @michieltcs
    #StandWithUkraine
    AGGREGATES
    @Aggregate
    class BankAccount {
    @AggregateIdentifier
    private lateinit var accountId: String
    private lateinit var accountNumber: String
    private lateinit var balance: BigDecimal
    @CommandHandler
    fun depositMoney(command: DepositMoney) {
    apply(MoneyDeposited(
    command.accountId,
    command.amount,
    GenericEventMessage.clock.instant()))
    }
    @EventHandler
    fun moneyDeposited(event: MoneyDeposited) {
    balance = balance.add(event.amount)
    }
    }

    View Slide

  14. @michieltcs
    #StandWithUkraine
    AGGREGATE STATE
    Account number Balance
    12345678 € 0,00
    Account number Balance
    12345678 € 100,00
    Account number Balance
    12345678 € 50,00
    event

    handler
    event

    handler
    event

    handler
    Money Withdrawn
    Account Id 1234
    Amount € 50,00
    Money Deposited
    Account Id 1234
    Amount € 100,00
    Account Opened
    Account Id 1234
    Account number 12345678

    View Slide

  15. @michieltcs
    #StandWithUkraine
    VALIDATING COMMANDS
    @CommandHandler
    @Throws(OverdraftDetectedException::class)
    fun withdrawMoney(command: WithdrawMoney) {
    if (balance.compareTo(command.amount) >= 0) {
    apply(MoneyWithdrawn(
    command.accountId,
    command.amount,
    GenericEventMessage.clock.instant()))
    } else {
    throw OverdraftDetectedException(accountNumber, balance, command.amount)
    }
    }

    View Slide

  16. @michieltcs
    #StandWithUkraine
    TESTING AGGREGATES
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    class BankAccountTest {
    private lateinit var fixture: AggregateTestFixture
    @BeforeEach
    fun createFixture() {
    fixture = AggregateTestFixture(BankAccount::class.java)
    }
    @Test
    fun noOverdraftsOnEmptyAccount() {
    fixture.given(accountOpened())
    .`when`(WithdrawMoney(ACCOUNT_ID, WITHDRAW_AMOUNT))
    .expectException(OverdraftDetectedException::class.java)
    }
    const val ACCOUNT_ID = "accountId"
    const val ACCOUNT_NUMBER = "3856625"
    }

    View Slide

  17. @michieltcs
    #StandWithUkraine
    THIS IS ABOUT


    CHANGE

    View Slide

  18. @michieltcs
    #StandWithUkraine
    -AG I L E M A N I F ES TO
    "Welcome changing requirements,
    even late in development."

    View Slide

  19. @michieltcs
    #StandWithUkraine
    MODIFICATIONS

    View Slide

  20. @michieltcs
    #StandWithUkraine
    EVENT SOURCING

    View Slide

  21. @michieltcs
    #StandWithUkraine
    CHALLENGE!

    View Slide

  22. @michieltcs
    #StandWithUkraine
    REPLAYS AND
    REBUILDS

    View Slide

  23. @michieltcs
    #StandWithUkraine
    ANSWERING
    QUERIES

    View Slide

  24. @michieltcs
    #StandWithUkraine
    BASED ON EVENTS

    View Slide

  25. @michieltcs
    #StandWithUkraine
    QUERIES
    ACCOUNT OPENED
    ACCOUNT OPENED
    ACCOUNT CLOSED Number of active accounts?

    View Slide

  26. @michieltcs
    #StandWithUkraine
    QUERIES
    MONEY DEPOSITED
    MONEY WITHDRAWN
    INTEREST RECEIVED
    Accounts with balance > €100?
    MONEY DEPOSITED
    MONEY WITHDRAWN

    View Slide

  27. @michieltcs
    #StandWithUkraine
    PROJECTION
    ACCOUNT OPENED EVENT HANDLER
    # OF ACTIVE
    ACCOUNTS +1
    ACCOUNT CLOSED EVENT HANDLER
    # OF ACTIVE
    ACCOUNTS -1

    View Slide

  28. @michieltcs
    #StandWithUkraine
    PROJECTION
    EVENTS EVENT HANDLER(S) STORAGE

    View Slide

  29. @michieltcs
    #StandWithUkraine
    CQRS

    View Slide

  30. @michieltcs
    #StandWithUkraine
    UI

    View Slide

  31. @michieltcs
    #StandWithUkraine
    DOMAIN
    UI
    COMMAND HANDLERS
    commands
    AGGREGATES

    View Slide

  32. @michieltcs
    #StandWithUkraine
    DOMAIN
    UI
    COMMAND HANDLERS
    REPOSITORY
    EVENT
    STORE
    commands
    events
    AGGREGATES

    View Slide

  33. @michieltcs
    #StandWithUkraine
    DOMAIN
    UI
    EVENT BUS
    EVENT HANDLERS
    COMMAND HANDLERS
    REPOSITORY
    DATABASE DATABASE
    EVENT
    STORE
    AGGREGATES
    commands
    events
    events

    View Slide

  34. @michieltcs
    #StandWithUkraine
    DOMAIN
    UI
    EVENT BUS
    EVENT HANDLERS
    COMMAND HANDLERS
    REPOSITORY
    DATA LAYER
    DATABASE DATABASE
    EVENT
    STORE
    commands
    events
    events
    queries DTOs
    AGGREGATES

    View Slide

  35. @michieltcs
    #StandWithUkraine
    PROJECTION
    class BankAccountProjections {
    private val activeAccounts: MutableMap = HashMap()
    @EventHandler
    fun onAccountOpened(accountOpened: AccountOpened) {
    val bankAccount = BankAccount(
    accountOpened.accountId,
    accountOpened.accountNumber,
    BigDecimal.ZERO)
    activeAccounts[accountOpened.accountId] = bankAccount
    }
    @EventHandler
    fun onAccountClosed(accountClosed: AccountClosed) {
    activeAccounts.remove(accountClosed.accountId)
    }

    View Slide

  36. @michieltcs
    #StandWithUkraine
    PROJECTION
    fun findAccountById(accountId: String): Optional {
    return Optional.ofNullable(activeAccounts[accountId])
    }
    fun getNumberOfAccounts(): Int {
    return activeAccounts.size
    }
    }

    View Slide

  37. @michieltcs
    #StandWithUkraine
    PROJECTION
    @GetMapping("accounts/{accountId}")
    fun getAccountNumber(@PathVariable("accountId") accountId: String?):
    Optional {
    return bankAccountProjections.findAccountById(accountId!!)
    .map { obj: BankAccount -> obj.accountNumber }
    }

    View Slide

  38. @michieltcs
    #StandWithUkraine
    PROJECTION
    @GetMapping("accounts/active")
    fun getNumberOfAccounts(): Int {
    return bankAccountProjections.getNumberOfAccounts()
    }

    View Slide

  39. @michieltcs
    #StandWithUkraine
    NEW PROJECTION

    View Slide

  40. @michieltcs
    #StandWithUkraine
    NEW STRUCTURE

    View Slide

  41. @michieltcs
    #StandWithUkraine
    BASED ON EXISTING
    EVENTS

    View Slide

  42. @michieltcs
    #StandWithUkraine
    REBUILDING
    STOP APP CLEANUP
    LOOP OVER
    EVENTS
    APPLY TO
    PROJECTION START APP

    View Slide

  43. @michieltcs
    #StandWithUkraine
    ZERO DOWNTIME
    LOOP OVER
    EXISTING EVENTS
    APPLY TO NEW
    PROJECTION USE PROJECTION

    View Slide

  44. @michieltcs
    #StandWithUkraine
    ZERO DOWNTIME
    NEW EVENTS QUEUE
    LOOP OVER
    EXISTING EVENTS
    APPLY TO NEW
    PROJECTION
    APPLY QUEUED
    EVENTS USE PROJECTION

    View Slide

  45. @michieltcs
    #StandWithUkraine
    LONG RUNNING
    REBUILDS

    View Slide

  46. @michieltcs
    #StandWithUkraine
    DISTRIBUTED

    View Slide

  47. @michieltcs
    #StandWithUkraine
    DIVIDING THE WORK
    EVENT
    EVENT
    EVENT
    EVENT
    EVENT
    AGGREGATE
    EVENT

    View Slide

  48. @michieltcs
    #StandWithUkraine
    DIVIDING THE WORK
    EVENT
    EVENT
    EVENT
    EVENT
    EVENT
    AGGREGATE
    INSTANCE
    EVENT
    INSTANCE

    View Slide

  49. @michieltcs
    #StandWithUkraine
    DIVIDING THE WORK
    EVENT
    EVENT
    EVENT
    EVENT
    EVENT
    AGGREGATE
    INSTANCE
    EVENT
    INSTANCE

    View Slide

  50. @michieltcs
    #StandWithUkraine
    DIVIDING THE WORK
    EVENT
    EVENT
    EVENT
    EVENT
    EVENT
    AGGREGATE
    INSTANCE
    EVENT
    EVENT
    EVENT
    EVENT
    INSTANCE

    View Slide

  51. @michieltcs
    #StandWithUkraine
    DIVIDING THE WORK
    EVENT
    EVENT
    EVENT
    EVENT
    EVENT
    AGGREGATE
    INSTANCE
    EVENT
    EVENT
    EVENT
    INSTANCE
    EVENT
    EVENT
    EVENT
    EVENT

    View Slide

  52. @michieltcs
    #StandWithUkraine
    DIVIDING THE WORK
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    SELLER LISTING
    Seller Name Listing Date Listing Description
    ... ... ...
    ... ... ...
    ... ... ...
    ... ... ...
    ... ... ...
    ... ... ...
    ... ... ...

    View Slide

  53. @michieltcs
    #StandWithUkraine
    DIVIDING THE WORK
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    SELLER LISTING
    INSTANCE INSTANCE

    View Slide

  54. @michieltcs
    #StandWithUkraine
    DIVIDING THE WORK
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    SELLER EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    LISTING EVENT
    SELLER LISTING
    INSTANCE INSTANCE
    ?

    View Slide

  55. @michieltcs
    #StandWithUkraine
    BACKGROUND
    TASKS

    View Slide

  56. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  57. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  58. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  59. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  60. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  61. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT
    TOKEN STORE

    View Slide

  62. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  63. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  64. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  65. @michieltcs
    #StandWithUkraine
    TRACKING EVENTS
    EVENT EVENT EVENT EVENT EVENT EVENT . . . . . . . . . . . . . .
    EVENT
    EVENT
    EVENT

    View Slide

  66. @michieltcs
    #StandWithUkraine
    TRACKING EVENT PROCESSOR
    GET NEXT EVENT
    APPLY TO NEW
    PROJECTION
    LAST EVENT? USE PROJECTION
    yes
    no

    View Slide

  67. @michieltcs
    #StandWithUkraine
    TRACKING EVENT PROCESSOR
    @Target(AnnotationTarget.CLASS)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class RebuildableProjection(
    val version: String = "",
    val rebuild: Boolean = false
    )

    View Slide

  68. @michieltcs
    #StandWithUkraine
    TRACKING EVENT PROCESSOR
    @Autowired
    fun startTrackingProjections(configurer: EventProcessingConfigurer) {
    val scanner = ClassPathScanningCandidateComponentProvider(false)
    scanner.addIncludeFilter(AnnotationTypeFilter(RebuildableProjection::class.java))
    for (bd in scanner.findCandidateComponents("org.demo")) {
    val projectionClass = Class.forName(bd.beanClassName)
    val rebuildableProjection =
    projectionClass.getAnnotation(RebuildableProjection::class.java)
    if (rebuildableProjection.rebuild) {
    registerRebuildableProjection(
    configurer,
    projectionClass,
    rebuildableProjection)
    }
    }
    }

    View Slide

  69. @michieltcs
    #StandWithUkraine
    TRACKING EVENT PROCESSOR
    private fun registerRebuildableProjection(configurer: EventProcessingConfigurer,
    projectionClass: Class<*>,
    rebuildableProjection: RebuildableProjection) {
    val processingGroup = projectionClass.getAnnotation(ProcessingGroup::class.java)
    val name = processingGroup?.let(ProcessingGroup::value)
    ?: (projectionClass.name + "/" + rebuildableProjection.version)
    configurer.assignHandlerTypesMatching(name) { eventHandler ->
    projectionClass.isAssignableFrom(ClassUtils.getUserClass(eventHandler))
    }
    configurer.registerTrackingEventProcessor(name)
    }

    View Slide

  70. @michieltcs
    #StandWithUkraine
    TRACKING EVENT PROCESSOR
    trackingEventProcessor.shutDown()
    trackingEventProcessor.resetTokens()
    trackingEventProcessor.start()

    View Slide

  71. @michieltcs
    #StandWithUkraine
    TRACKING EVENT PROCESSOR
    class BankAccountProjections {
    private val activeAccounts: MutableMap = HashMap()
    @ResetHandler
    fun resetProjections() {
    activeAccounts.clear()
    }
    ////

    }

    View Slide

  72. @michieltcs
    #StandWithUkraine
    EVENT VERSIONING

    View Slide

  73. @michieltcs
    #StandWithUkraine
    NEW BUSINESS
    REQUIREMENTS

    View Slide

  74. @michieltcs
    #StandWithUkraine
    CHANGING VIEW ON
    EVENTS

    View Slide

  75. @michieltcs
    #StandWithUkraine
    IRRELEVANT

    View Slide

  76. @michieltcs
    #StandWithUkraine
    DIFFERENT FIELDS

    View Slide

  77. @michieltcs
    #StandWithUkraine
    WRONG NAME

    View Slide

  78. @michieltcs
    #StandWithUkraine
    TOO COARSE

    View Slide

  79. @michieltcs
    #StandWithUkraine
    TOO FINE

    View Slide

  80. @michieltcs
    #StandWithUkraine
    HOW TO SUPPORT
    YOUR LEGACY?

    View Slide

  81. @michieltcs
    #StandWithUkraine
    Commands
    can be
    renamed
    1

    View Slide

  82. @michieltcs
    #StandWithUkraine
    Commands
    can be
    renamed
    1
    Events are
    immutable
    2

    View Slide

  83. @michieltcs
    #StandWithUkraine
    Commands
    can be
    renamed
    1
    Events are
    immutable
    Correct old
    events with
    new events
    2 3

    View Slide

  84. @michieltcs
    #StandWithUkraine
    Ledger Entry
    Aug 14 Inventory € 15600,00
    Accounts Payable € 15600,00

    View Slide

  85. @michieltcs
    #StandWithUkraine @michieltcs
    Ledger Entry
    Aug 14 Inventory € 15600,00
    Accounts Payable € 15600,00
    Ledger Entry
    Aug 14 Inventory € 16500,00
    Accounts Payable € 16500,00

    View Slide

  86. @michieltcs
    #StandWithUkraine
    Ledger Entry
    Aug 14 Inventory € 15600,00
    Accounts Payable € 15600,00
    Ledger Entry
    Aug 14 Inventory € 16500,00
    Accounts Payable € 16500,00
    Ledger Correction Entry
    Aug 14 Inventory € 900,00
    Accounts Payable € 900,00

    View Slide

  87. @michieltcs
    #StandWithUkraine
    COMPENSATING ACTIONS
    class MoneyWithdrawn {

    String accountId;

    BigDecimal amount;

    }
    class WithdrawalRolledBack {

    String accountId;

    BigDecimal amount;

    }
    Typo: too much withdrawn!

    View Slide

  88. @michieltcs
    #StandWithUkraine
    COMPENSATING ACTIONS
    class AccountOpened {

    String accountId;

    String accountNumber;

    }
    class DuplicateAccountClosed {

    String accountId;

    }
    Duplicate account number!

    View Slide

  89. @michieltcs
    #StandWithUkraine
    UPCASTING

    View Slide

  90. @michieltcs
    #StandWithUkraine
    UPCASTING
    EVENT STORE
    OLD EVENT TYPE
    UPCASTER
    NEW EVENT TYPE
    EVENT HANDLER

    View Slide

  91. @michieltcs
    #StandWithUkraine
    UPCASTING
    EVENT STORE

    View Slide

  92. @michieltcs
    #StandWithUkraine
    UPCASTING
    @Revision("1.0")
    data class AccountOpened(
    val accountId: String,
    val accountNumber: String
    )

    View Slide

  93. @michieltcs
    #StandWithUkraine
    UPCASTING


    80f49161

    12345678



    View Slide

  94. @michieltcs
    #StandWithUkraine
    UPCASTING
    EVENT STORE
    ACCOUNTOPENED (1.0)
    EVENT HANDLER

    View Slide

  95. @michieltcs
    #StandWithUkraine
    UPCASTING
    @Revision("2.0")
    data class AccountOpened(
    val accountId: String,
    val accountNumberIban: String
    )

    View Slide

  96. @michieltcs
    #StandWithUkraine
    UPCASTING


    80f49161

    NL00ABNA012345678


    View Slide

  97. @michieltcs
    #StandWithUkraine
    UPCASTING
    EVENT STORE
    ACCOUNTOPENED (1.0)
    UPCASTER
    ACCOUNTOPENED (2.0)
    EVENT HANDLER

    View Slide

  98. @michieltcs
    #StandWithUkraine
    UPCASTING
    class AccountOpenedUpcaster : SingleEventUpcaster() {
    private val typeConsumed = SimpleSerializedType("AccountOpened", "1.0")
    private val typeProduced = SimpleSerializedType("AccountOpened", "2.0")
    override fun canUpcast(intermediateRepresentation:
    IntermediateEventRepresentation): Boolean {
    return intermediateRepresentation.type == typeConsumed
    }
    override fun doUpcast(intermediateRepresentation:
    IntermediateEventRepresentation): IntermediateEventRepresentation {
    return intermediateRepresentation.upcastPayload(
    typeProduced,
    JsonNode::class.java
    ) { event: JsonNode ->
    val node = event as ObjectNode
    val accountNumber = node["accountNumber"].asText()
    node.put("accountNumberIban", toIban(accountNumber))
    node.remove("accountNumber")
    event
    }
    }

    View Slide

  99. @michieltcs
    #StandWithUkraine
    VERSIONED EVENT
    STORE

    View Slide

  100. @michieltcs
    #StandWithUkraine
    events_v1
    [

    {

    "id": "12345678",

    "type": "AccountOpened",

    "aggregateType": "Account",

    "aggregateIdentifier": "1234",

    "sequenceNumber": 0,

    "payloadRevision": "1.0",

    "payload": { ... },

    "timestamp": ...

    ...

    },

    ...

    ]

    View Slide

  101. @michieltcs
    #StandWithUkraine
    COPY & REPLACE

    View Slide

  102. @michieltcs
    #StandWithUkraine
    VERSIONED EVENT STORE
    LOOP OVER
    EXISTING EVENTS APPLY UPCASTER
    ADD QUEUED
    EVENTS
    USE NEW EVENT
    STORE
    NEW EVENTS QUEUE

    View Slide

  103. @michieltcs
    #StandWithUkraine
    events_v2
    [

    {

    "id": "12345678",

    "type": "AccountOpened",

    "aggregateType": "Account",

    "aggregateIdentifier": "1234",

    "sequenceNumber": 0,

    "payloadRevision": "2.0",

    "payload": { ... },

    "timestamp": ...

    ...

    },

    ...

    ]

    View Slide

  104. @michieltcs
    #StandWithUkraine
    GDPR

    View Slide

  105. @michieltcs
    #StandWithUkraine
    "RIGHT TO
    ERASURE"

    View Slide

  106. @michieltcs
    #StandWithUkraine
    - G D P R , A RT I C L E 17
    "... shall have the right to obtain ...
    the erasure of personal data
    concerning him or her without undue
    delay"

    View Slide

  107. @michieltcs
    #StandWithUkraine
    PERSONALLY
    IDENTIFIABLE
    INFORMATION

    View Slide

  108. @michieltcs
    #StandWithUkraine
    REMOVED

    View Slide

  109. @michieltcs
    #StandWithUkraine
    ANONYMIZED?

    View Slide

  110. @michieltcs
    #StandWithUkraine
    PROCESSING GDPR ART. 17 REQUESTS
    RIGHTTOERASUREINVOKED
    REMOVE FROM
    EVENT STORE
    REMOVE FROM
    READ MODELS
    NOTIFY 3RD
    PARTIES

    View Slide

  111. @michieltcs
    #StandWithUkraine
    PROCESSING GDPR ART. 17 REQUESTS
    RIGHTTOERASUREINVOKED
    REMOVE FROM
    EVENT STORE
    ?
    REMOVE FROM
    READ MODELS
    NOTIFY 3RD
    PARTIES

    View Slide

  112. @michieltcs
    #StandWithUkraine
    IMMUTABLE
    EVENTS?

    View Slide

  113. @michieltcs
    #StandWithUkraine
    MODIFY DIRECTLY

    View Slide

  114. @michieltcs
    #StandWithUkraine
    COPY WITH FILTER

    View Slide

  115. @michieltcs
    #StandWithUkraine
    STORE PII
    EXTERNALLY

    View Slide

  116. @michieltcs
    #StandWithUkraine
    STORE PII EXTERNALLY
    ACCOUNTOPENED EXTERNAL STORAGE
    Account Id


    Account number Name
    1234 12345678 John Doe
    ... ... ...
    data class AccountOpened(
    @TargetAggregateIdentifier

    val accountId: String
    )

    View Slide

  117. @michieltcs
    #StandWithUkraine
    CRYPTO SHEDDING

    View Slide

  118. @michieltcs
    #StandWithUkraine
    ENCRYPTING EVENTS


    80f49161

    NL00ABNA012345678

    Foo

    Bar


    ...


    View Slide

  119. @michieltcs
    #StandWithUkraine
    ENCRYPTING EVENTS


    80f49161

    2dqjHkY8Mc8+cek4vs/9hzgkob4J3fZJNIJh2sAXlJ0=
    accountNumberIban>

    N5Y27vd0UbKo6FIu5c7QGQ==

    OSKrzfuuuayuUNXYS5YUug==


    ...


    View Slide

  120. @michieltcs
    #StandWithUkraine
    ENCRYPTING EVENTS
    GENERATE
    EVENT
    FIND /
    CREATE
    ENCRYPTION
    KEY
    ENCRYPT
    PAYLOAD
    VALUES
    STORE

    EVENT

    View Slide

  121. @michieltcs
    #StandWithUkraine
    DECRYPTING EVENTS
    LOAD

    EVENT
    FIND
    ASSOCIATEDENC
    RYPTION KEY
    DECRYPT
    PAYLOAD
    VALUES
    PROCESS

    EVENT

    View Slide

  122. @michieltcs
    #StandWithUkraine
    SHEDDING THE KEY
    LOAD

    EVENT
    FIND
    ASSOCIATEDENC
    RYPTION KEY
    DECRYPT
    PAYLOAD
    VALUES
    PROCESS

    EVENT
    X

    View Slide

  123. @michieltcs
    #StandWithUkraine
    OPTIONS
    https://developer.axoniq.io/axon-data-protection/overview


    https://github.com/everest-engineering/axon-crypto-shredding-extension

    View Slide

  124. @michieltcs
    #StandWithUkraine
    CLOSING WORDS

    View Slide

  125. @michieltcs
    #StandWithUkraine
    NO SILVER BULLET

    View Slide

  126. @michieltcs
    #StandWithUkraine
    LOTS OF
    CHALLENGES

    View Slide

  127. @michieltcs
    #StandWithUkraine
    (IM)MUTABILITY

    View Slide

  128. @michieltcs
    #StandWithUkraine
    AUDIT TRAIL

    View Slide

  129. @michieltcs
    #StandWithUkraine
    SCALABILITY

    View Slide

  130. @michieltcs
    #StandWithUkraine
    TESTING

    View Slide

  131. @michieltcs
    @michieltcs
    #StandWithUkraine
    THANK YOU FOR
    LISTENING!


    twitter: @michieltcs

    mastodon: @[email protected]

    email: [email protected]


    www.michielrook.nl

    View Slide