CQRS+ES+GDPR+PHP = Broadway Sensitive Serializer

Matteo Galacci
5 min readApr 8, 2023

--

In this post I want to share my experience with GDPR , specifically the right to be forgotten, and the CQRS+ES architecture in PHP. Let’s start from some basic concepts.

CQRS

CQRS (Command Query Responsibility Segregation) is a pattern that aims to separate responsibilities for queries (the read side) and for commands (write side). It is a pattern that separates the read and write operations on a given model. This, in practice, leads to different concrete objects, separated in write models and read models. So, this pattern can lead to different tables or data stores where the data is separated based on whether it is a command or a query. Separation apart, the last state of the model will still be persisted as it happens in traditional CRUD systems.

Event Sourcing

ES (Event Sourcing), used together with CQRS, “transforms” the writing side of the CQRS models into a succession of events that are persisted in an Event Store, a specific table or data store that acts as a chronological and immutable register of events. The idea is that commands executed on a model lead to the issuance of events, which are stored in the Event Store. All events issued by a Model are persisted in this table, with a specific incremental index, sometimes called a playhead, that represents the order in which the events have been issued. For each new Model (Aggregate), its events start with playhead = 0.” The Event Store is a recording system for all events that, if reapplied to the model in the same order of generation, bring it to its last state. Alternatively, it may be possible to see a previous state of the model. These events can then be projected as views (Read Models) or generate new commands (Processor) if listened to by specific Listeners. The views will then be the models used by the read queries, which are persisted in tables other than the Event Store or even in a different Data Store.

https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcing

Event Store immutability

So using the whole CQRS+ES pattern, we have an Event Store in which all events will be written in chronological order and grouped for each model using an aggregate ID. The Event Store is immutable by its nature; after writing an event, it can never be changed. If necessary, compensation events will be issued to compensate for the previous events. Imagine a bank account and its list of transactions, and think of a compensation event as a reversal.

Projections

In a CQRS+ES system, there are usually projections. If the event store is the chronological register of all the writing operations that took place on a specific Aggregate, then a projection is a specific view of the data. For a single Aggregate, we could have as many views as we need. So, after an event is issued, an event listener could listen to that event in order to project a view of it. Multiple event listeners can listen to the same event to project different representations of the same dataset.

Art. 17 GDPR — Right to be forgotten

The lay says: The data subject shall have the right to obtain from the controller the erasure of personal data concerning him or her without undue delay and the controller shall have the obligation to erase personal data without undue delay… Read the complete legislation

CQRS+ES+GDPR

We have stated that in the CQRS+ES pattern, the Event Store is immutable. However, to comply with GDPR regulations, users may request the deletion of their data. This may seem paradoxical, as deleting a user’s data in a CQRS+ES system would require either deleting events from the Event Store or modifying existing events, both of which are not possible. Compensation events are also not a solution in this case, as we could always recover the user’s data by going back in history.

Broadway — PHP library for CQRS+ES

Broadway is a project providing infrastructure and testing helpers for creating CQRS and event sourced applications. Broadway tries hard to not get in your way. The project contains several loosely coupled components that can be used together to provide a full CQRS\ES experience.

Broadway Sensitive Serializer

The proposal

Broadway Sensitive Serializer is a PHP extension for Broadway that offers a solution to the challenge of implementing CQRS+ES+GDPR in PHP.

Event Store

Instead of thinking in terms of deleting or modifying events, the idea is to persist from the very beginning of the history, events in which payload (user information, or in general the event containing sensitive data) is encrypted by an encryption key specific for each Aggregate. As long as the key is present, the data can be encrypted and decrypted. When the key will be deleted (following a user request), the events will remain in the Event Store, but the payload, originally encrypted, will remain encrypted without the possibility of decryption. Thus, the story will remain unchanged, but the data is not understandable.

Broadway standard payload

{
"class": "SensitiveUser\\User\\Domain\\Event\\UserRegistered",
"payload": {
"id": "b0fce205-d816-46ac-886f-06de19236750",
"name": "Emmett",
"surname": "Brown",
"email": "emmet@brown.dr"
"occurred_at": "2022-01-08T14:22:38.065+00:00",
}
}

Sensitized payload

{
"class": "SensitiveUser\\User\\Domain\\Event\\UserRegistered",
"payload": {
"id": "b0fce205-d816-46ac-886f-06de19236750",
"name": "Emmett",
"surname": "#-#2Iuofg4NKKPLAG2kdJrbmQ==:bxQo+zXfjUgrD0jHuht0mQ==",
"email": "#-#OFLfN9XDKtWrmCmUb6mhY0Iz2V6wtam0pcqs6vDJFRU=:bxQo+zXfjUgrD0jHuht0mQ==",
"occurred_at": "2022-01-08T14:22:38.065+00:00",
}
}

Projections

The sensitization operation is performed at a different time from the event projection, so the views will have the data decrypted to allow the read operations to work correctly. When a user makes use of the right to be forgotten, you should do three things:

  1. Delete his encryption key
  2. Delete the views that contain his data
  3. Re-project events to regenerate views with encrypted data. (This will be easy as since there is no encryption key for a specific Aggregate, reading it from the Event Store will be hydrated with the sensitized data. This obviously involves particular checks in the Value Objects or in the Aggregate itself)
Broadway sensitive serializer event store representation

Important note

It’s important to understand that the idea behind this project is not about general security or data leak. The idea behind this implementation is rather to make a CQRS + ES system compliant with the user’s right of asking at any time to be forgotten, while keeping the system consistent.

Of course, you can also use this library in a different context of GDPR law, as this library basically does nothing but decorate the Broadway serializer, giving it the ability to encrypt and decrypt the payload of events.

Resources

--

--

Matteo Galacci

Software architect & Senior back-end software engineer