Broadway concepts and proposal

Aggregate and persistence

Since this wiki is not meant to be a complete manual on the concepts in question, we will just call the Model, Aggregate and remind ourselves that the Aggregate, as such, is the source of our domain events; a client will ask the User Aggregate to create a new user, which will not only create the instance, but also the related event, UserCreated.

class BroadwayUsers extends EventSourcingRepository implements Users
{
    public function add(User $user): void
    {
        parent::save($user);
    }
}

class User extends EventSourcedAggregateRoot
{
    public static function crea(
            UserId $userId,
            string $name,
            string $surname,
            string $email,
            DateTimeImmutable $regDate
    ): self
    {
        $user = new self();

        $user->apply(new UserCreated($userId, $name, $surname, $email, $regDate));

        return $user;
    }
}

$user = User::create($userId, $name, $surname, $email, $registrationDate);

$users->add($user);

Event serialization

When we ask Broadway to persist an Aggregate, the EventSourcingRepository takes all the events not yet committed from the Aggregate and asks the specific implementation of the Event Store to serialize them and then save them. For example, in the case of Broadway DBALEventStore:

private function insertMessage(Connection $connection, DomainMessage $domainMessage): void
{
    $data = [
        'uuid' => $this->convertIdentifierToStorageValue((string) $domainMessage->getId()),
        'playhead' => $domainMessage->getPlayhead(),
        'metadata' => json_encode($this->metadataSerializer->serialize($domainMessage->getMetadata())),
        'payload' => json_encode($this->payloadSerializer->serialize($domainMessage->getPayload())),    // <-----
        'recorded_on' => $domainMessage->getRecordedOn()->toString(),
        'type' => $domainMessage->getType(),
    ];

    $connection->insert($this->tableName, $data);
}

When instantiating the EventSourcingRepository you need to inject a serializer ($this->payloadSerializer) which in the case of the default Broadway implementation is a SimpleInterfaceSerializer which implements the Broadway\Serializer interface. SimpleInterfaceSerializer does nothing but call the Broadway\Serializer::serialize($object): array or Broadway\Serializer::deserialize(array $serializedObject) method on the event to be serialized in the case of reading from the Event Store, where it is necessary to recreate the event starting from the payload.

Let’s focus for now on the Broadway\Serializer::serialize($object): array method which, as read from the signature, returns an array which is later converted to json thanks to PHP’s json_encode() function.

Proposal implementation

It is precisely on the serializer that this library intervenes. The idea is to decorate the native Broadway serialization by adding the ability to encrypt and decrypt (sensitize and desensitize) the payloads of the events, or rather the values of its keys, based on 3 strategies that we will see later, Whole strategy, Partial Strategy and Custom strategy. Therefore, when a new aggregate is created, a specific key will be generated which will be used to encrypt and decrypt.

{
    "class": "SensitiveUser\\User\\Domain\\Event\\UserRegistered",
    "payload": {
        "id": "446effc9-4f5c-4369-8e89-91cb5c8509b9",
        "occurred_at": "2022-01-08T14:22:38.065+00:00",
        "name": "Matteo",
        "surname": "Galacci",
        "email": "m.galacci@gmail.com"
    }
}

Double key encryption

As mentioned above, when a new Aggregate is created, its key is also created and persisted in the appropriate table. Each aggregate has its own key so that it can invalidate individual Aggregates upon request. To improve security, the key of the aggregate, which we will call AGGREGATE_KEY, is in turn encrypted with what we will call AGGREGATE_MASTER_KEY.

AGGREGATE_KEY

  • It persisted in the database and is in a 1:1 relationship with the aggregate.

  • It is encrypted with the AGGREGATE_MASTER_KEY. This is to prevent events from being decrypted following a database violation.

  • It can be deleted so as to make the Aggregate no longer decryptable

AGGREGATE_MASTER_KEY

  • It is one for all AGGREGATE_KEY.

  • It is not persisted in the database. It is set in an environment variable or otherwise on the server. More drivers will be available in the future to get the key.