Skip to content

Migrer vers le Event Sourcing, partie 4: garder trace des changements

by Julien on août 3rd, 2010

An english version of this post is available here.

Vue d’ensemble

Cette étape n’apportera aucun avantage technique. Le but est plutôt de préparer les 2 prochaines étapes. Nous voulons dans cette partie garder la trace des changements qui ont lieu dans nos agrégats:

Vous avez probablement déjà deviné que nous stoquerons ces chagements sous forme des évènements qui ont eu lieu dans les agrégats. Chaque agrégat sera responsable de garder la trace des évènements qui sont arrivés depuis le moment où l’agrégat a été chargé de la base de donnée.

Avantages

Communication

Comme pour les commandes, les évènements devraient faire partie du “ubiquitous language”. C’est un outil qui pourrait vous aider à bien identifier ce qui arrive dans le domaine lorsqu’une commande est exécutée.

Ménage

Comme vous le verrez plus bas, définir les évènements explicitement va vous obliger à respecter une règle du DDD qui est souvent oubliée: la limite (ou enveloppe?) de cohérence (“consistency boundary” en anglais).

Implémentation

La première chose à faire est évidemment d’ajouter une liste des évènements à nos racines d’agrégat:

public interface IAggregateRoot
{
    Guid Id { get; }
    IEnumerable<IEvent> UncommitedEvents { get; }
}

Nous avons aussi un projet additionnel pour accueillir les évènements. Ce projet ne DOIT PAS avoir de dépendance sur le modèle du domaine:

Une manière de s’assurer que les évènements décrivent effectivement ce qu’il s’est passé dans le domaine est de déléguer les changements d’état de nos agrégats à une méthode qui ne prends que l’évènement en paramètre. Par exemple:

public virtual void RegisterTo(Class @class)
{
    // Business rules, some validation here

    // State changes
    ApplyEvent(new StudentRegisteredToClassEvent(Id, @class.Id, @class.credits));
}

private void Apply(StudentRegisteredToClassEvent evt)
{
    registrationSequence = registrationSequence.Next();
    registrations.Add(new Registration(this, registrationSequence.ToId(), evt.ClassId, evt.Credits));
}

Si vous avez regarder le modèle de domaine de mon petit exemple dans les étapes précédente, vous aurez noté que j’ai corrigé un défaut dans l’entité Registration. Voici l’entité telle que présente dans les étapes précédentes (constructeur caché pour plus de clarté):

public class Registration : Entity<Student>
{
    internal Class _class;
}

Un Enregistrement est une partie de l’agrégat Etudiant. Il ne devrait pas référencer directement un autre agrégat telle que Classe. Nous aurions du à la place définir dans l’entité uniquement ce qui est necessaire à l’application des règles d’affaires d’Etudiant:

public class Registration : Entity<Student>
{
    private Guid classId;
    private int classCredits;
}

Cette refactorisation nous permet d’avoir un StudentRegisteredClassEvent. Puisque les évènements n’ont pas de dépendance vers le modèle de domain, celui ci n’aurait pu porter un objet Class. La deuxième version cependant rends le contenu de l’évènement évident:

public class StudentRegisteredToClassEvent : IEvent
{
    public readonly Guid StudentId;
    public readonly Guid ClassId;
    public readonly int Credits;

}

AggregateRoot.ApplyEvent()

Les méthodes Apply(SomeEvent evt) ne sont pas appelées directement. A la place, la racine d’agrégat appelle void ApplyEvent<T>(T evt). Cette méthode va bien sûr appeler la méthode Apply(SomeEvent evt) correspondante, mais aussi rajouter l’évènement à la liste des évènements non comités.

En aparté, l’implémentation de void ApplyEvent<T>(T evt) n’utilise pas la réflexion pour appeler Apply(SomeEvent evt). Pour plus de performance, elle utilise la sympathique API des arbres d’expression pour construire une liste de délégués au démarrage.

Crédits

  • Pour l’implémentation originale de la méthode ApplyEvent(): Greg Young
  • Pour la mise en valeur des termes du modèle de domaine: Richard Dingwall

Démarrer l’application exemple

Les sources pour toute la série de billets sont disponible sur GitHub à http://github.com/jletroui/TransitioningToEventSourcing
.

Vous aurez simplement besoin de créer une base de données vide intitulée “DDDPart4″ dans SQLExpress avant de lancer l’application.

Liste des billets “migrer vers le Event Sourcing”:

From → Event Sourcing

10 Comments
  1. Remi Despres-Smyth permalink

    I’ve really been enjoying this series. It’s exactly the sort of thing I’ve been looking for: contrasting how I more-or-less currently build applications with event sourcing, to allow me to gain a concrete end-to-end understanding of the architecture and how it works.

    I’m really hoping you’re just gone on vacation and have not dropped the series entirely?

    Best regards,
    Remi.

  2. Julien permalink

    Yep, I am just back from my vacations… I will continue the serie very soon.

  3. Lek permalink

    Bonjour,
    je viens de dévorer les 4 posts que tu as écris sur le sujet et tenais absolument à te remercier pour ce travail remarquable. Vraiment merci de reprendre et clarifier l’event sourcing (et en français!!).
    J’espère que les futures articles arriverons bientôt.

    Encore merci,
    Lek.

Trackbacks & Pingbacks

  1. Transitioning to Event Sourcing, part 2: go CQRS with DTOs | Julien's blog
  2. Transitioning to Event Sourcing, part 3: commands | Julien's blog
  3. Transitioning your DDD “light” application to CQRS and Event Sourcing | Julien's blog
  4. Transitioning to Event Sourcing, part 1: the DDD “light” application | Julien's blog
  5. Transitioning to Event Sourcing, part 5: use events for updating your domain database | Julien's blog
  6. Transitioning to Event Sourcing, part 6: store events | Julien's blog
  7. Transitioning to Event Sourcing, part 7: build a view model | Julien's blog

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS