Skip to content
mars 7 11

Visual Studio Talk Show sur CQRS

by Julien

Le podcast Visual Studio Talk Show (qui est un podcast en français en dépit de son nom) m’a invité pour parler de CQRS et Event Sourcing. Vous pouvez écouter l’émission ici.

janv. 19 11

Code source de l’atelier CQRS

by Julien

Pour ceux qui n’ont pu être présent à l’atelier de samedi, les exercices et leur corrections sont disponible sur Git Hub:

https://github.com/jletroui/CQRSWorkshop

Les diapos du meetup du 15 Decembre sont également disponibles ici.

janv. 3 11

Atelier CQRS / Event Sourcing à Montréal le 15 janvier

by Julien

Suivant le meetup de décembre sur le sujet, le groupe des developpeurs montréalais vous permet de coder une application CQRS / Event Sourcing lors d’un atelier d’une journée le 15 Janvier.

Les détails pratiques et l’inscription sont disponible sur EventBrite.

déc. 2 10

DDD / CQRS / Event Sourcing meetup à Montréal le 15 décembre

by Julien

Je vais présenter une introduction et tenter de répondre au maximum de questions sur DDD / CQRS / Event Sourcing à Montréal (en français). Cela couvrira les concepts mais surtout les avantages que cette architecture peut apporter à une équipe et une entreprise.

Et pour être concret, ce meetup sera suivi en Janvier d’un atelier d’une journée où vous pourrez essayer, construire et voir en action cette architecture.

Les détails pratiques et les inscriptions sont sur EventBrite.

oct. 24 10

Migrer vers le Event Sourcing, partie 5: persister à l’aide des événements

by Julien

An english version of this post is available here.

Vue d’ensemble

L’étape précédente a introduit un problème. Nous avons maintenant 2 modèles représentant nos aggrégats: la base de données relationnelle, et nos événements. Pouvons nous être sûr que ces deux modèles sont cohérents? Non, car il n’y a aucun lien entre la base de données maintenue par NHibernate et nos événements:

C’est pourtant critique, car si nous voulons migrer au event sourcing, nous voulons nous assurer que les événements que nous générons reflètent bien le contenu de la base de données. La solution est de simplement utiliser nos événements pour persister les changements dans la base de données:

C’est l’objet de ce billet.

Avantages

Cohérence

Les événements sont maintenant la source des changements d’état dans nos aggrégats et dans la base de données.

Performance

Nous n’avons plus besoin de laisser NHibernate calculer les changements dans la session. En effet, le flush de session NHibernate est coûteux: NHibernate doit comparer l’état de chaque entité avec son état original afin de calculer les requêtes SQL nécessaires à la persistance des entités.

En utilisant les handlers d’événements, nous n’avons plus besoin de flusher la session NHibernate, car les événements décrivent déjà les changements d’état. Nous exécutons donc simplement des requêtes SQL spécifiques à chaque événement.

Implémentation

Ce qui a changé

Les handlers d’événements de persistance ont leur propre projet:

Unité de travail

Le premier élément de ce changement est le repository. Celui ci doit maintenant garder la liste des agrégats chargés et créés:

public T ById(Guid key)
{
    var resVal =  persistenceManager.CurrentSession.Get<T>(key);
    AddToContext(resVal);
    return resVal;
}

public void Add(T toAdd)
{
    AddToContext(toAdd);
}

private void AddToContext(T toAdd)
{
    HashSet<IAggregateRoot> aggregates = context[NHibernatePersistenceManager.AGGREGATE_KEY] as HashSet<IAggregateRoot>;

    if (aggregates == null)
    {
        aggregates = new HashSet<IAggregateRoot>();
        context[NHibernatePersistenceManager.AGGREGATE_KEY] = aggregates;
    }

    aggregates.Add(toAdd);
}

Il devient donc facile de persister les changements lors d’un commit, en appelant simplement les handlers:

public void Commit()
{
    var aggregates = context[AGGREGATE_KEY] as HashSet<IAggregateRoot>;

    if (aggregates != null && aggregates.Count > 0)
    {
        var session = EnsureOpened();

        using (var tx = session.Connection.BeginTransaction(IsolationLevel.ReadCommitted))
        {
            context[TRANSACTION_KEY] = tx;

            foreach (var ar in aggregates)
            {
                foreach (var evt in ar.UncommitedEvents)
                {
                    eventBus.Publish(evt);
                }
            }

            context[TRANSACTION_KEY] = null;
            tx.Commit();

            context[AGGREGATE_KEY] = null;
        }
    }
}

Handlers d’événements

L’implémentation des handlers est triviale. Par exemple:

public void Handle(StudentNameCorrectedEvent evt)
{
    persistenceManager.ExecuteNonQuery(
        "UPDATE [Student] SET firstName = @FirstName, lastName = @LastName WHERE Id = @Id",
        new
        {
            Id = evt.StudentId,
            FirstName = evt.FirstName,
            LastName = evt.LastName
        });
}

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 “DDDPart5″ dans SQLExpress avant de lancer l’application.

Liste des billets “migrer vers le Event Sourcing”:

oct. 21 10

Mono et MonoTouch Meetup + AutoTest Demo

by Julien

A l’occasion de la conférence Unite 10JB Evain est présent à Montréal. Développeur chez Novell il est le créateur de la librairie Mono.Cecil très connue des manipulateurs de code IL.

On va donc profiter de cet évènement pour mieux connaître Mono mais surtout découvrir les dessous de MonoTouch l’environnement de développement  .NET pour iPhone ainsi que son petit frère MonoDroid pour la plate-forme Android. (cet évènement sera en français)

En marge de l’évènement Greg Young présentera AutoTest.NET, un framework de build/test très automatisé qui s’intègre à tout environnement Linux/Vim à Windows/Visual Studio (cet évènement sera en anglais)

Attention le nombre de places est limité !

Register for Mono et MonoTouch Meetup + AutoTest Demo in Montreal, Quebec  on Eventbrite

août 3 10

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

by Julien

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”:

juil. 22 10

Migrer vers le Event Sourcing, partie 3: les commandes

by Julien

An english version of this post is available here.

Vue d’ensemble

Cette refactorisation est plus subtile que la précédente. Dans la partie 1, j’ai déjà expliqué qu’il était important d’avoir une méthode de racine d’agrégat par cas d’utilisation. Nous allons maintenant faire un pas de plus en créant une classe pour chacun de ces cas, sous forme de commandes:

Avantages

Isolation

L’interface utilisateur n’a maintenant plus de dépendance sur le modèle de domaine. Les deux peuvent évoluer séparément sans s’impacter mutuellement.

Communication

Créer des classes pour les commande, c’est aussi reconnaître que ces dernières sont une partie importante du modèle de domaine et du “Ubiquitous Language”. Souvent, les utilisateurs vont parler assez simplement de ce qu’ils attendent de l’application:

  • Je veux créer des étudiants.
  • Je veux créer des classes.
  • Je veux inscrire des étudiants dans des classes.

Les commandes vont formaliser ces besoins.

Nouvelles opportunités

Avoir en main les commandes dans le système ouvre certaines opportunités. D’abord, il devient très facile de logguer les commandes. En faisant cela, vous captureriez tout ce que les utilisateurs ont voulu modifier dans l’application.
Le découplage entre l’exécution de la commande et l’interface utilisateur ouvre aussi des choix. Dans l’exemple de ce post, les commandes sont exécutées immédiatement, dans le thread du contrôleur. Mais il serait facile d’en déléguer l’exécution sur un serveur applicatif (par exemple en utilisant NServiceBus ). Cela permet de décharger vos serveurs web et de monter en charge plus facilement à coût très faible. De plus, vous n’avez pas besoin de faire ce choix maintenant. Il ne coûtera pas plus cher lorsque vous en aurez besoin.

Implémentation

Ce qui a changé

Il y a 2 nouveaux projets dans la solution:

Le premier contient les commandes, et l’autre les gestionnaires de commandes. Un gestionnaire de commande est responsable d’appeler la racine d’agrégat appropriée pour traiter la commande.

Les commandes sont dispatchées à leur gestionnaires par un bus.

Envoyer une commande

Au final, le code du contrôleur devient encore plus simple:

public RedirectToRouteResult DoCorrectName(StudentDTO model)
{
    var cmd = new CorrectStudentNameCommand(model.Id, model.FirstName, model.LastName);
    commandBus.Send(cmd);

    return RedirectToRoute(new
    {
        controller = "Student",
        action = "Index"
    });
}

Le bus va simplement transmettre la commande à la méthode correspondante dans le gestionnaire:

public void Handle(CorrectStudentNameCommand cmd)
{
    var student = studentRepository.ById(cmd.StudentId);

    if (student != null)
    {
        student.CorrectName(cmd.FirstName, cmd.LastName);
    }
}

Conseils

Les conseils de la partie 1 s’appliquent aussi ici:

  • Une commande ne doit s’applique qu’à une racine d’agrégat.
  • Une seule méthode d’agrégat doit être appelée pour traiter une commande.

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 “DDDPart3″ dans SQLExpress avant de lancer l’application.

Liste des billets “migrer vers le Event Sourcing”:

juil. 15 10

Migrer vers le Event Sourcing, partie 2: Etre CQRS en utilisant des DTOs pour les requêtes

by Julien

An english version of this post is available here.

Vue d’ensemble

Pour cette première étape de refactorisation, nous voulons utiliser CQRS (séparation des commandes et des requête en français). CQRS veut simplement dire que le composant que l’on utilise pour aller chercher de l’information à afficher (une requête) est différent du composant qui permet d’effectuer une action (une commande). C’est d’ailleurs une bonne pratique à toutes les échelles. Vos classes seront plus facile à maintenir si vos méthodes sont séparées en 2 groupes disctincts comme le suggère Bertrand Meyer:

  • Celles qui retourne void: elles effectuent une action qui change l’état interne de la classe. Elles renvoient des exceptions en cas d’erreur.
  • Celles qui retournent d’autre chose que void: elles renvoient une information, et ne sont pas autorisées à modifier l’état de l’objet

En d’autre termes, poser une question ne devrait pas modifier la réponse. Comme Greg Young le précise, c’est très important dans le cas où vous utilisez des contrats pour vos méthodes: si vous utilisez une méthode qui retourne une information pour vérifier une pré condition, et que votre méthode modifie l’état de l’objet, vous allez vous retrouver avec des situations intéressantes à gérer.

A l’échelle de l’architecture de l’application, cela signifie que nous n’allons plus utiliser NHibernate pour accéder à nos données d’affichage:

Au lieu d’afficher les entités, nous allons afficher des DTOs (Objets de Transfert de Données en français). Un DTO est un simple objet avec des propriétés publiques et aucun comportement associé. Contrairement aux entités qui sont designée pour optimiser l’implémentation des règles d’affaires, les DTOs sont spécialement créés pour optimiser l’affichage. En fait, on peut même dire que les DTOs sont une modélisation logique des données affichées à l’écran. De plus, un DTO contient toutes les données d’un écran, chargées avec une seule requête à la base de données. Dans notre cas, les DTOs sont extraits de la base relationnelle transactionnelle, les DTOs seront donc chargés à partir de requêtes SQL.

Quoi, une requête SQL ? Mais les MOR n’étaient pas sensé nous protéger d’une telle antiquité? En fait, oui, mais cela a un prix. Un prix que nous ne voulons plus payer.

Quel problème y a t-il à utiliser HQL ou Linq pour faire une requête? Tout d’abord, comme dit plus haut, les entités sont optimisées pour traiter les commandes de l’usager. Elles évitent donc la duplication de données, ce qui se traduit en base de données par un schéma de forme normée 3 la plupart du temps. Or les écrans affichent des données qui souvent necessitent plusieurs agrégats. Par example, si vous afficher un étudiant, vous voudrez certainement afficher le nom des classes auxquelles il est inscrit. Cela signifie que votre MOR favoris a besoin de charger les associations, ce qui se fait de façon différé (lazy load en anglais). Si vous ne faites pas attention, vous allez donc faire beaucoup de requêtes en base de données pour chaque écran. De même, vous allez probablement charger un certain nombre de propriétés dont vous n’avez pas besoin. Bien sûr, vous pouvez utiliser des stratégies de chargement (fetching strategies en anglais). Ces stratégies vous permettent pour chaque requête de préciser quelles sont les associations que vous voulez charger immédiatement. Mais cela requiert un niveau de complexité supplémentaire dans votre infrastructure. Finalement, vous aurez certainement besoin de transformer les données de vos entités pour les adapter aux besoin de chaque écran. Mais le plus gros problème est que vos entités ont besoin de supporter l’affichage. Au lieu de se concentrer uniquement sur l’exécution des règles d’affaires. Or les 2 fonctions ont des contraintes très différentes. Idéalement, vous voulez afficher un écran de votre application avec le moins de requêtes possible à votre source de données. Idéalement, les données que vous allez chercher devraient être dénormalisées, afin d’éviter au maximum les jointures. Par contre, du coté transactionnel, vous voulez probablement avoir un modèle de données de forme normée 3.

Séparer le coté transactionnel du coté requêtage permet d’optimiser les 2 cotés indépendamment. Assez parlé, voyons comment faire ce pas important. Regardons une action qui affiche les détails d’un agrégat:

public ViewResult Details(Guid studentId)
{
    return View(studentQueries.ById(studentId));
}

A première vue, peu de chose ont changé. Excepté que l’on utilise plus le repository de l’agrégat pour faire la requête. L’objet retourné n’est plus une entité, mais un DTO.

Avantages

Simplicité

Il n’y a plus besoin de stratégies de chargement, ni de mapping entre les entités et les modèles de vue. Vous n’avez plus besoin que d’une simple requête SQL pour chaque vue.

Plus de getters dans votre domaine!

Vous venez juste de libérer votre domaine des contraintes liées à l’affichage. Dans l’immédiat, cela vous permet de supprimer vos getters dans vos entités. Nous verrons plus tard que cela ouvre d’autres opportunités.

Implémentation

Ce qui change

Il y a un nouveau projet dans la solution pour le modèle de vue:

Ce projet contiens les DTOs ainsi que leur requêtes.

Les repository ont disparu:

En parlant des repository, la méthode IPaginable<T> All() a elle aussi disparue de l’interface.

L’implémentation NHibernate du IPaginable<T> a été remplacée par une implémentation SQL pure (attention, la pagination n’est supportée qu’à partir de SQL Server 2005).

De plus, n’étant pas un fan du SQL dans le code source, un petit composant permet de les écrire dans un fichier XML simple (plus sur le sujet plus tard).

Finalement, un nouveau composant fait son apparition, le mappeur de DTO. Ce composant mappe par convention les résultats d’un IDataReader dans des objets DTOs

Créer un DTO

Dans ce mini framework, un DTO est une simple classe avec un constructeur vide et des propriétés publiques. Par exemple:

public class ClassDTO
{
    public Guid Id { get; set; }
    [Required]
    [StringLength(255, MinimumLength = 1)]
    public string Name { get; set; }
    [Required]
    [Range(3, 6)]
    public int Credits { get; set; }
}

Vous pouvez noter que puisque j’utilise mes DTOs comme modèle de vue également, ils sont l’endroit idéalement pour placer un peu de validation.

Créer une requête

Voici une interface de requête simple:

public interface IClassDTOQueries
{
    IPaginable<ClassDTO> All();
}

Et voici l’implémentation:

public class SQLClassDTOQueries : DTOQueries, IClassDTOQueries
{
    public SQLClassDTOQueries(IPersistenceManager pm)
    : base(pm)
    {
    }

    public IPaginable<ClassDTO> All()
    {
        return ByNamedQuery<ClassDTO>("All", null);
    }
}

La méthode ByNamedQuery() prends 3 arguments:

  • Le nom de la requête (à aller chercher dans le fichier XML)
  • Les paramètres de la requête
  • (Optionnel) La liste des collections du DTO à remplir

Vous pouvez regarder dans l’application exemple pour des requêtes qui utilisent toutes ces fonctions.

Finalement, vous pouvez définir la requête elle même dans un fichier XML qui porte le même nom que votre implémentation:

<?xml version="1.0" encoding="utf-8" ?>
<queries>
<query name="All" defaultSort="Id">
<count>SELECT COUNT(*) FROM Class</count>
<select>
SELECT class.Id,
class.name as Name,
class.credits as Credits
FROM Class class
</select>
</query>
</queries>

Pour chaque requête, vous devez définir:

  • La requête qui compte le nombre total d’éléments (nécessaire pour la pagination).
  • La requête allant chercher les éléments (la “vraie” requête).

Pour les requêtes destinées à retourner un seul élément, la requête qui compte est optionnelle.

Le mappeur de DTO utilisera l’alias des colonnes pour trouver la propriété correspondante dans le DTO. Le mappeur de DTO supporte les propriétés simple, les sous DTOs et la plupart des collections.

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 “DDDPart2″ dans SQLExpress avant de lancer l’application.

Liste des billets “migrer vers le Event Sourcing”:

juil. 13 10

Migrer vers le Event Sourcing, partie 1: l’application DDD “légère”

by Julien

An english version of this post is available here.

  • Part 1: the DDD “light” application.

    Ce billet va présenter un exemple d’une architecture essayant de supporter le DDD. Vous pouvez télécharger l’application complète ici. Vous pouvez utiliser ce code comme bon vous semble, y compris pour des applications commerciales fermées. Mais si vous la publiez, vous devez me mentionner dans les crédits. Bien sûr, je vous déconseille d’utiliser le code du présent billet, puisque le but des prochains billets est de présenter ce que je pense est une meilleure alternative.

    Ce premier billet est long, puisque j’ai besoin de décrire toute l’architecture. Les prochains seront plus succincts, puisqu’ils décriront une évolution à la fois.

    Cet exemple est écrit en C#, avec NHibernate comme ORM, Windsor Castle comme Container d’IdC, et ASP.Net MVC pour l’interface usager.

    Je vais supposer que vous êtes déjà familier avec les concepts de Mapping Objet-Relationnel, d’Inversion de Contrôle, et du patron Modèle-Vue-Contrôleur.

    L’architecture (simplifiée) ressemble à ceci:

    Rentrons maintenant dans les détails.

    1 – Le coté “requêtage”

    Quand vous voulez afficher de l’information dans cette application simple, vous faites une requête sur vos entité. Habituellement, vous le faites en utilisant le patron “repository”.Par exemple :

    public interface IStudentRepository : IRepository<Student>
    {
        IPaginable<Student> ByNameLike(string name);
    }

    L’interface de base ressemble à:

    public interface IRepository<T> where T : IAggregateRoot
    {
        T ById(Guid id);
        void Add(T toAdd);
        void Remove(T toRemove);
        IPaginable<T> All();
    }

    L’idée derrière un repository est (entres autres) de grouper toutes les requêtes à votre entité. Les MOR comme NHibernate ou le Framework ADO.Net Entity facilitent l’implémentation.

    Vous avez sans doute remarqué que les requêtes pouvant retourner plusieurs entités renvoient un IPaginable<T>. L’idée est que vous avez besoin de paginer la requête, mais sans exposer un IQueryable<T> ce qui amènerait immanquablement votre code à être parsemé de requêtes. Encore une fois, dans mon expérience, il est bien plus maintenable de regrouper les requêtes dans un seul repository. L’interface du  IPaginable<T> ressemble à ceci:

    public interface IPaginable<T>
    {
        int Count();
        T UniqueValue();
        IEnumerable<T> ToEnumerable();
        IEnumerable<T> ToEnumerable(int skip, int take);
        IEnumerable<T> ToEnumerable(int skip, int take, string sortColumn,  SortDirection? sortDirection);
    }

    Tout comme avec un IQueryable<T>, il est facile d’implémenter un proxy de pagination pour l’interface usager comme le IPagination<T> de la librairie MvcContrib. Le code de vos actions dans vos contrôleurs devient trés simple. Par exemple:

    public ViewResult Index(int Page = 1, string Name = null)
    {
        var model = new StudentSearchModel()
        {
            Name = Name,
            Students = studentRepository.ByNameLike(Name).AsPagination(Page)
        };

        return View(model);
    }

    public ViewResult Details(Guid studentId)
    {
        var student = studentRepository.ById(studentId);

        return View(student);
    }

    Et voilà pour l’affichage!

    2 – Le coté transactionnel

    Évidemment, la plupart des applications ne se contentent pas d’afficher des données. Elles implémentent généralement des règles d’affaire, des processus et des calculs. Essayons de voir comment cela fonctionne ici.

    La règle est que pour chaque action que l’utilisateur a besoin d’effectuer, il doit y avoir une et une seule méthode dans la racine de l’agrégat correspondant. Si vote interface supporte les mises à jours en batch, alors cette même méthode peut être appelée sur plusieurs instances de l’agrégat.

    Cela rends le code de vos contrôleurs très simple. Par exemple, pour assigner une classe à un étudiant:

    public RedirectToRouteResult DoRegisterToClass(RegisterToClassModel  model)
    {
        var student = studentRepository.ById(model.StudentId);
        var @class = classRepository.ById(model.ClassId);
        student.RegisterTo(@class);
        return RedirectToRoute(new
        {
            controller = "Student",
            action = "Index"
        });
    }

    Qu’arrive t-il dans l’agrégat étudiant?

    public virtual void RegisterTo(Class @class)
    {
        // Business rules
        @class.Validation().NotNull("class");
        if (registrations.Where(x => x.Class.Id == @class.Id).Count() > 0)
        {
            throw new InvalidOperationException("You can not register a student to a class he already registered");
        }
        if (passedClasses.Where(x => x == @class.Id).Count() > 0)
        {
            throw new InvalidOperationException("You can not register a student to a class he already passed");
        }

        // State changes
        registrationSequence = registrationSequence.Next();
        registrations.Add(new Registration(registrationSequence.ToId(), @class));
    }

    Vous pouvez voir que toutes les méthodes publique de racine d’agrégat sont divisées en 2 parties:

    • Validation des règles d’affaire. Cette partie vérifie que l’action demandée peut être effectuées. Cette partie peut lancer des exceptions.
    • Changement de l’état de l’agrégat. Cette partie effectue l’action. Comme les règles d’affaire ont été vérifiée, cette partie ne peut lancer d’exception.

    L’idée est de ne pas modifier une entité tant que l’on est pas certain de pouvoir la mettre à jour avec succès. Nous allons voir dans les prochains billets que cela facilite les refactorisations futures.

    Vous avez peut être remarqué qu’aucun code ici ne fait appel directement à NHibernate ou à une transaction quelconque. C’est parce que cette application utilise le patron “session par requête”. Le code qui ouvre et ferme la session NHibernate se trouve dans un IHttpModule:

    void context_BeginRequest(object sender, EventArgs e)
    {
        CurrentContainer.Container.Build<IPersistenceManager>().Open();
    }

    void context_EndRequest(object sender, EventArgs e)
    {
        var pm = CurrentContainer.Container.Build<IPersistenceManager>();

        try
        {
            if (HttpContext.Current.Error == null) pm.Commit();
        }
        finally {pm.Close();}
    }

    Le IPersistenceManager est une simple abstraction du mécanisme de persistance.

    Et voilà pour le coté transactionnel!

    3 – Racines d’agrégat et entités

    Il y a quelques subtilités dans l’implémentation avec NHibernate des agrégats.

    Pas de setters!

    Les setters sont un anti patron en général, et plus encore en DDD. DDD ne se concentre pas principalement sur la structure des données (les noms). La méthodologie permet de modéliser des comportements, ce qui s’exprime avant tout par des verbes. Donc, l’interface publique de vos agrégats devrait ne contenir que des méthodes, chacune correspondant à un cas d’utilisation. Si vous ressentez le besoin d’appeler un setter dans un contrôleur ou un service applicatif, c’est probablement que vous manquez un cas d’utilisation pour cet agrégat. Un des effets de bord positif de cette règle est qu’il devient très facile de s’assurer que votre agrégat reste en tout temps parfaitement valide, et que tous les invariants soient respectés en tout temps.

    Les plus perspicaces noterons que si DDD se concentre sur les comportements, alors les getters aussi devraient être des anti patron. C’est le cas. Malheureusement, nous avons encore besoin des getters car nous utilisons les entités pour l’interface usager. Nous verrons dans le prochain billet comment nous en débarrasser.

    Identifiant des racines d’agrégat

    Les racines devraient avoir un identifiant universel, c’est à dire un identifiant qui peut être utilisé partout dans votre compagnie, et même par vos client et fournisseurs. On peut donc oublier le brave entier séquentiel ici. La meilleure option est probablement le Guid. De plus, un Guid a le bon goût de pouvoir être généré directement dans le code du domaine, sans avoir besoin de faire une requête à votre base de donnée, et sans verrou global. De cette manière, vous serrez prêts pour vous intégrer avec d’autre systèmes plus tard, à la SOA.

    Identifiant des autres entités

    Si vous avez lu le livre d’Eric Evans, vous savez que toutes les entités doivent avoir un identifiant. Ce que ne dit pas clairement le livre cependant, c’est que les entités à l’intérieur d’un agrégat peuvent avoir un identifiant local à l’agrégat. De cette manière, vous n’avez plus besoin de verrou global pour générer vos identifiants. Vous pourriez là aussi utiliser un Guid mais vous ne voulez probablement pas payer le prix de génération et de stockage d’un guid pour chacune de vos sous-entités. Un simple entier fera l’affaire ici.

    Générer les identifiants

    Utiliser les générateurs d’identifiants de votre base de données (@@identity en SQL Server et les séquences dans Oracle) est une mauvaise idée. Cela consomme un appel à la dite base de donnée, et necessite un verrou global. 2 problèmes qui vont limiter votre capacité à monter en charge.

    Donc, le code pour générer un identifiant d’agrégat est simple:

    public abstract class AggregateRoot : IAggregateRoot
    {
        private DateTime? version = null;

        public AggregateRoot()
        {
            Id = Guid.NewGuid();
        }

        public virtual Guid Id {get; private set;}
    }

    Pour les identifiants d’entités, vous pouvez utiliser la technique décrite ici:

    public abstract class Entity<T> where T : AggregateRoot
    {
    protected DateTime? version = null;

    // NHibernate constructor.

    protected Entity() { }

    public Entity(T aggregateRoot, int id)
    {
    this.aggregateRoot =  aggregateRoot.Validation().NotNull("aggregateRoot");
    Id = id;
    }

    private T aggregateRoot;
    public virtual int Id {get; private set;}
    }

    La clef primaire d’une entité sera donc une composition de la clef de l’agrégat, puis de la clef “locale” de l’entité.

    C’est la responsabilité de la racine de l’agrégat de générer ces identifiants. Par exemple:

    private IdSequence registrationSequence;

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

        // State changes
        registrationSequence = registrationSequence.Next();
        registrations.Add(new Registration(registrationSequence.ToId(),  @class));
    }

    L’avantage est que vous associez ces identifiant directement dans le constructeur. Vous pouvez donc déjà les utiliser pour des associations faibles (= sans clef étrangère dans la base de données). Le problème est que cela peut générer des problèmes de concurrence. Lorsque 2 entités sont créées au même moment dans le même agrégat par 2 threads différents, vous obtenez un bris de clef primaire. Dans ce cas, la victime doit mettre à jour sa clef, et recommencer. Nous allons voir que le event sourcing va nous offrir une solution plus élégante à ce problème.

    Voilà, c’est tout pour l’application de départ. Le prochain billet parlera d’optimiser le coté requêtage de l’application.

    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 “DDDPart1″ dans SQLExpress avant de lancer l’application.

    Crédits

    • Pour l’idée et l’implémentation originelle du paginable et du repository: Benoit Goudreault-Emond
    • Pour les identifiants locaux dans les entités: Greg Young

    Liste des billets “migrer vers le Event Sourcing”: