Partager via



Juin 2016

Volume 31, numéro 6

Cet article a fait l'objet d'une traduction automatique.

À la pointe - Générer un historique CRUD, 2e partie

Par Dino Esposito | Juin 2016

Dino EspositoPoint de vue conceptuel, un historique création, de lecture, de mise à jour, suppression (CRUD) est le standard CRUD étendu avec un paramètre supplémentaire : une date. Un historique CRUD permet d’ajouter, mettre à jour et supprimer des enregistrements d’une base de données et vous permet d’interroger l’état de la base de données à un moment donné dans le temps. Un historique CRUD donne à vos applications une infrastructure intégrée pour l’analyse business intelligence et des fonctionnalités de création de rapports avancées.

Dans la rubrique du mois dernier (msdn.com/magazine/mt703431), j’ai présenté la Fondation théorique de systèmes CRUD historiques. Dans cet article, je vais donner une démonstration pratique.

Présentation de l’exemple de scénario

Dans le cadre de cet article, je vais envisager un système de réservation simple. Par exemple, prendre le système, qu'une entreprise utilise en interne pour vos employés les salles de réunion de livre. Au final, ce logiciel est un CRUD simple où un nouvel enregistrement est créé pour réserver un emplacement. Le même enregistrement est mis à jour si la réunion est déplacée vers une autre heure ou supprimé si la réunion est annulée.

Si vous codez un système de réservation comme un CRUD régulière, vous connaissez le dernier état du système, mais perdez les informations sur les mises à jour et supprimées des réunions. Est-ce vraiment un problème ? Cela dépend. Il n’est probablement pas un problème si vous examinez simplement les effets des réunions sur l’activité réelle. Toutefois, si vous recherchez des moyens d’améliorer les performances des employés, puis un historique CRUD qu’assure le suivi des mises à jour et suppressions d’enregistrements risque de révéler que trop de fois réunions sont déplacées ou annulées et qui peuvent être le signe moins optimal des processus internes ou attitude incorrecte.

Figure 1 présente une interface utilisateur réaliste pour un système de réservation d’espace. La base de données sous-jacente est une base de données SQL Server avec deux tables liées : Salles et les réservations.

L’interface utilisateur frontale pour un système de réservation
Figure 1 l’interface utilisateur frontale pour un système de réservation

L’exemple d’application est conçue comme une application ASP.NET MVC. Lorsque l’utilisateur clique pour placer la requête, une méthode de contrôleur intervient et traite les informations validées. L’extrait de code suivant donne une idée claire du code qui gère la requête du côté serveur :

[HttpPost]
public ActionResult Add(RoomRequest room)
{
  service.AddBooking(room); 
  return RedirectToAction("index", "home");
}

La méthode appartienne à une classe BookingController et délègue à une classe worker injecté le fardeau d’organiser le travail réel. Un aspect intéressant de l’implémentation de la méthode est qu’il redirige vers la page avant de Figure 1 après avoir créé la réservation. Il n’existe aucune vue explicite en cours de création en tant que le résultat de l’opération de réservation de l’ajouter. Il s’agit d’un effet secondaire du choix d’une architecture de commandes requête répartition (CQRS). La commande add-réservation est publiée sur le serveur principal ; Il modifie l’état du système, et c’est tout. L’exemple d’application avait utilisé AJAX pour valider, il aurait été inutile pour actualiser tout élément et la commande aurait été une opération autonome sans lien visible à l’interface utilisateur.

La différence principale entre une CRUD classique et un historique CRUD est que ce dernier effectue le suivi de toutes les opérations qui modifient l’état du système depuis le début. Pour planifier un historique CRUD, vous devez considérer que les commandes que vous donnez au système et d’un mécanisme pour effectuer le suivi de ces commandes vers le bas de l’entreprise. Chaque commande modifie l’état du système et un historique CRUD assure le suivi de chaque état qu'atteint le système. Tout état atteint est enregistrée comme un événement. Un événement est la description d’un élément simple et immuable qui s’est produit. Une fois que la liste des événements, vous pouvez créer plusieurs projections de données par-dessus, le plus courant est simplement l’état actuel des entités métier impliqués.

Dans une application, les événements proviennent directement à partir de l’exécution de commandes de l’utilisateur, ou indirectement d’autres commandes ou une entrée externe. Dans ce scénario, l’utilisateur clique sur un bouton pour publier une demande de réservation est censé.

Traitement de la commande

Voici une implémentation possible de la méthode AddBooking du contrôleur de l’application :

public void AddBooking(RoomRequest request)
{
  var command = new RequestBookingCommand(request);
  var saga = new BookingSaga();
  var response = saga.AddBooking(command);
  // Do something based on the outcome of the command
}

La classe RoomRequest est un objet de transfert de données brut rempli par la couche de liaison d’ASP.NET MVC hors des données publiées. La classe RequestBookingCommand, stocke au lieu de cela, les paramètres d’entrée requis pour exécuter la commande. Dans un tel scénario simple, les deux classes presque coïncident. Comment traiterait la commande ? Figure 2 présente les étapes clé trois du traitement d’une commande.

La chaîne d’étapes de base pour traiter une commande
Figure 2 la chaîne d’étapes de base pour traiter une commande

Le gestionnaire est un composant qui reçoit la commande et le traite. Un gestionnaire peut être appelé via un appel direct en mémoire à partir de dans le code de service de travail, ou vous pouvez avoir un bus intermédiaire, comme illustré ici :

public void AddBooking(RoomRequest request)
{
  var command = new RequestBookingCommand(request);
  // Place the command on the bus for
  // registered components to pick it up
  BookingApplication.Bus.Send(command);
}

Un bus susceptibles de se présenter quelques avantages. Une est que vous pouvez gérer facilement les scénarios dans lesquels plusieurs gestionnaires peuvent être intéressées par la même commande. Un autre avantage est qu’un bus peut être configuré pour être un outil de messagerie fiable qui garantit la remise du message dans le temps et la résolution des problèmes de connectivité possibles. En outre, un bus peut être simplement le composant qui offre la capacité d’enregistrer la commande.

Le gestionnaire peut être un simple composant unique qui commence et se termine dans la même demande, ou il peut être un flux de travail durable qui prend des heures ou des jours et peut être suspendu en attente d’approbation humaine à un moment donné. Les gestionnaires qui ne sont pas unique guère exécuteurs sont souvent appelés sagas.

En général, vous utilisez un bus ou une file d’attente si vous avez des exigences spécifiques en termes d’évolutivité et de fiabilité. Si vous recherchez uniquement pour la création d’un historique CRUD à la place un CRUD classique, vous avez probablement pas besoin d’utiliser un bus. Si vous utilisez un bus ou non, à un moment donné la commande pouvant atteindre son gestionnaire unique ou à long terme. Le gestionnaire est censé pour effectuer toutes les tâches qui sont attendus. La plupart des tâches se composent des principales opérations sur une base de données.

Journalisation de la commande

Dans un CRUD classique, écrire des informations dans une base de données signifie qu’Ajout d’un enregistrement qui présente les valeurs passées. Dans un point de vue historique CRUD, cependant, le dernier enregistrement ajouté représente les événements d’une réservation. Les événements d’une réservation est une information indépendante et immuable des informations qui inclut un identificateur unique pour l’événement, un horodatage, un nom et une liste de spécifiques des arguments de l’événement. Les arguments d’un événement créé incluent généralement toutes les colonnes à remplir pour un enregistrement de réservation qui vient d’être ajouté dans une table classique de réservations. Les arguments d’événement mis à jour, au lieu de cela, sont limitées aux champs qui sont réellement mises à jour. Par la suite, tous les événements de mise à jour ne disposent ne peut-être pas le même contenu. Enfin, les arguments d’un événement deleted sont limitées aux valeurs qui identifient de façon unique la réservation.

Toute opération d’un historique CRUD se compose de deux étapes :

  1. Enregistrer l’événement et ses données associées.
  2. Vérifiez que l’état actuel du système est immédiatement interrogeable et rapidement.

De cette façon, l’état actuel du système est toujours disponible et à jour et toutes les opérations qui ont conduit à celle-ci sont également disponibles pour toute une analyse plus approfondie. Notez que le « état actuel du système » est simplement l’état seulement que vous voyez dans un système CRUD classique. Pour être efficace dans le contexte d’un système CRUD simple, l’étape d’enregistrement de l’événement et la mise à jour de l’état du système doit avoir lieu de façon synchrone et dans la même transaction, comme indiqué dans Figure 3.

Figure 3 enregistrement d’un événement et de la mise à jour du système

using (var tx = new TransactionScope())
{
  // Create the "regular" booking in the Bookings table   
  var booking = _bookingRepository.AddBooking(
    command.RoomId, ...);
  if (booking == null)
  {
    tx.Dispose();   
    return CommandResponse.Fail;
  }
  // Track that a booking was created
  var eventToLog = command.ToEvent(booking.Id);
    eventRepository.Store(eventToLog);
  tx.Complete();
  return CommandResponse.Ok;
}

Comme les choses sont, chaque fois que vous ajoutez, modifiez ou supprimez un enregistrement de réservation que vous tenir à jour la liste globale des réservations à jour lors de la connaître l’ordre exact des événements qui ont conduit à l’état actuel. Figure 4 illustre les deux tables SQL Server impliqués dans l’exemple de scénario et leur contenu après une insertion et la mise à jour.

Les réservations et les Tables LoggedEvents côte à côte
Figure 4 les réservations et les Tables LoggedEvents côte à côte

Le tableau répertorie les réservations toutes les réservations distinctes trouvées dans le système et pour chaque retourne l’état actuel. La table LoggedEvents répertorie tous les événements pour les réservations différents dans l’ordre qu’ils ont été enregistrés. Par exemple, la réservation 54 a été créé sur une date donnée et modifié quelques jours plus tard. Dans l’exemple, la colonne de cargaison dans l’image stocke le flux JSON sérialisée de la commande en cours d’exécution.

À l’aide des événements enregistrés dans l’interface utilisateur

Supposons qu’un utilisateur autorisé souhaite afficher les détails d’une réservation en attente. Probablement l’utilisateur obtient à la prise en compte dans une liste de calendrier ou d’une requête basée sur le temps. Dans les deux cas, les faits fondamentales de réservation, quand, combien de temps et qui, sont déjà connus et les détails de l’affichage peut même être utiles. Il peut réellement être utile, cependant, si vous pouvez afficher l’historique complet de réservation, comme indiqué dans Figure 5.

Consommation d’événements enregistrés dans l’interface utilisateur
Figure 5 Utilisation d’événements enregistrés dans l’interface utilisateur

En lisant les événements enregistrés, vous pouvez créer un modèle de vue qui inclut une liste des États de la même entité d’agrégation : réservation #54. Dans l’exemple d’application, lorsque l’utilisateur clique pour afficher les détails de la prise en compte une fenêtre contextuelle modale s’affiche et certaines JSON est téléchargé en arrière-plan. Le point de terminaison qui renvoie le JSON est illustrée ici :

public JsonResult BookingHistory(int id)
{
  var history = _service.History(id);
  var dto = history.ToJavaScriptSlotHistory();
  return Json(dto, JsonRequestBehavior.AllowGet);
}

La méthode de l’historique sur le service de travail effectue l’essentiel du travail ici. La partie essentielle de ce travail interroge tous les événements liés à l’ID de réservation spécifié :

var events = new EventRepository().All(aggregateId);
foreach (var e in events)
{
  var slot = new SlotInfo();
  switch (e.Action)
  {
    :
  }
  history.Changelist.Add(slot);
}

Pendant que vous parcourez les événements enregistrés, un objet approprié est ajouté à l’objet de transfert de données à retourner. Certaines transformations effectuées dans le ToJavaScriptSlotHistory rend rapide et plus facile d’afficher le delta entre les deux États consécutifs dans le formulaire que vous voyez dans Figure 5.

Il est remarquable, cependant, que même au sein d’un CRUD d’enregistrement des événements permet de ces nouvelles améliorations très utiles dans l’interface utilisateur, la plus grande valeur tient au fait que vous savez tout ce qui sont passées au sein du système et pouvez traiter ces données pour extraire une projection personnalisée de données, que vous devrez peut-être à un moment donné. Par exemple, vous pouvez créer une statistique de la mise à jour et laisser les analystes viennent à la conclusion que l’ensemble du processus de demande de salles de réunion ne fonctionne pas dans l’entreprise, car les personnes trop souvent livre puis update ou delete. Vous pouvez également facilement suivre ce que la situation était des réservations à une date spécifique par interrogation simplement les événements connecté là et calcul de l’état des éléments suivant vers le bas. En bref, un historique CRUD ouvre un tout nouveau monde de possibilités pour les applications.

Synthèse

Historique CRUD est simplement une façon plus intelligente d’évolution des applications CRUD simples. Pourtant, cette discussion abordé jure et des modèles qui ont beaucoup plus logique potentiels, tels que la CQRS, source d’événement, au bus et files d’attente et messages de l’entreprise. Si vous avez trouvé cet article utile, je vous suggère de revenir en arrière et lire mon juillet 2015 (msdn.com/magazine/mt238399) et août 2015 (msdn.com/magazine/mt185569) colonnes. Dans cet exemple, vous pouvez trouver ces articles encore plus été !


Dino Espositoest l’auteur de « Microsoft .NET : Conception d’Applications pour l’entreprise » (Microsoft Press, 2014) et « Applications Web modernes avec ASP.NET » (Microsoft Press, 2016). Un développeur technique pour les plateformes .NET et Android chez JetBrains, dans le monde entier manifestations du secteur, Esposito partage sa vision du logiciel à software2cents@wordpress.com et sur Twitter : @despos.

Merci à l'experte technique Microsoft suivante d'avoir relu cet article : Jon Arne Saeteras