IT 02.Concetti Broadway e proposta - matiux/broadway-sensitive-serializer Wiki

Aggregato e persistenza

Dato che questa wiki non vuole essere un manuale completo sui concetti in questione, ci limiteremo a chiamare il Modello, Aggregato e a ricordarci che l'Aggregato, in quanto tale, è la sorgente dei nostri eventi di dominio; un client chiederà all'Aggregato User di creare un nuovo utente, richiesta che comporterà non solo la creazione dell'istanza, ma anche del relativo evento, 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);

Serializzazione degli eventi

Quando chiediamo a Broadway di persistere un Aggregato, l'EventSourcingRepository prende dall'Aggregato tutti gli eventi non ancora committati e chiede alla specifica implementazione dell'Event Store di serializzarli per poi salvarli. Ad esempio, nel caso nel caso di 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);
}

Quando si istanzia l'EventSourcingRepository è necessario iniettare un serializzatore ($this->payloadSerializer) che nel caso dell'implementazione di default di Broadway si tratta di SimpleInterfaceSerializer che implementa l'interfaccia Broadway\Serializer. SimpleInterfaceSerializer non fa altro che chiamare sull'evento da serializzare il metodo Broadway\Serializer::serialize($object): array o Broadway\Serializer::deserialize(array $serializedObject) nel caso si tratti di lettura dall'Event Store dove è necessario ricreare l'evento a partire dal payload.

Concentriamoci per ora sul metodo Broadway\Serializer::serialize($object): array che, come si legge dalla firma, ritorna un array che successivamente viene convertito in json grazie alla funzione json_encode() di PHP.

Implementazione della proposta

È proprio sul serializzatore che questa libreria interviene. L'idea è quella di decorare il serializzare nativo di Broadway aggiungendogli la capacità di criptare e decriptare (sensibilizzare e desensibilizzare) i payload degli eventi, o meglio le sue chiavi, in base a 2 strategie che vedremo successivamente, Whole strategy, Partial strategy e Custom strategy. Alla creazione di un nuovo aggregato quindi, verrà generata una chiave specifica che verrà utilizzata per criptare e decriptare.

Payload evento persistito da Broadway con driver DBAL

{
    "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": "[email protected]il.com"
    }
}

Payload evento persistito da Broadway con driver DBAL con Whole Strategy

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

Payload evento persistito da Broadway con driver DBAL con Custom Strategy

{
    "class": "SensitiveUser\\User\\Domain\\Event\\UserRegistered",
    "payload": {
        "id": "c9298698-b30e-40c5-8d85-624fdf57f9df",
        "occurred_at": "2022-01-08T14:26:39.483+00:00",
        "name": "Matteo",
        "surname": "Galacci",
        "email": "#-#aw+tw7shnEs2px030QS9WgRmGZckEGnIeR0a8ByMkPI=:Q0jkEOZtOs56tMkc8SjP5g=="
    }
}

Criptazione a doppia chiave

Come detto sopra, alla creazione di un nuovo Aggregato viene creata anche la sua chiave e persistita nell'apposita tabella. Ogni aggregato ha la sua chiave così da poter invalidare singoli Aggregati su richiesta. Per migliorare la sicurezza, la chiave dell'aggregato, che chiameremo AGGREGATE_KEY è a sua volta criptata tramite quella che chiameremo AGGREGATE_MASTER_KEY.

AGGREGATE_KEY

AGGREGATE_MASTER_KEY