
Partager:
James is a Microsoft MVP with a background in fintech & insurance industries building web and mobile applications. He's the author of Refactoring TypeScript and the creator of some open-source tools for .NET called Coravel. He lives in eastern Canada and is a 10-minute drive from the highest tides in the world!
Audit des appels téléphoniques avec Event Sourcing en .NET
Temps de lecture : 7 minutes
Les événements sont partout ! Le monde du développement logiciel a pris conscience des avantages qu'il y a à modéliser nos processus d'entreprise et notre logique d'application sous la forme d'un journal d'événements. L'approvisionnement en événements a gagné en popularité comme moyen de construire des systèmes qui protègent contre la perte de données, modélisent plus clairement des scénarios d'entreprise complexes et offrent une certaine flexibilité dans la manière dont ils peuvent être étendus.
Lorsque nous nous adressons à des experts en la matière, nous utilisons naturellement des événements pour décrire des scénarios d'entreprise. Par exemple, voici une brève discussion qui encadre le contexte de ce tutoriel :
Expert métier : "Lorsqu'un client passe un appel téléphonique, nous voulons vérifier si l'appel a été pris et quand il a été terminé."
Développeur : "D'accord, nous devons donc suivre le moment où les appels téléphoniques sont lancés, où l'on y répond et où ils sont terminés ?
Dans cette brève discussion sur la collecte des besoins, nous avons découvert trois événements différents que nous devrons capturer : l'appel a commencé, l'appel a répondu et l'appel s'est terminé.
Heureusement pour nous, Vonage dispose d'une fantastique API pour le suivi des appels téléphoniques!
Nous allons utiliser l'API Vonage et construire une application .NET Core qui stocke et affiche ces informations en utilisant le sourcing d'événements.
Code
Si vous souhaitez passer à l'étape suivante, vous pouvez consulter le code de ce tutoriel sur GitHub.
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
Conditions préalables
Pour commencer, vous aurez besoin de
Un IDE pour le développement .NET comme Visual Studio, Visual Studio Code ou JetBrains Rider.
La La dernière version de .NET / .NET Core installée
Un local Postgres
ngrok téléchargé et installé
Les bases de l'approvisionnement en événements
L'approvisionnement en événements est très différent de ce que nous avons l'habitude de faire dans l'industrie du logiciel.
Normalement, nous avons tendance à stocker l'état actuel de notre système dans des tables ou des documents de la base de données. Les données historiques sont stockées séparément.
L'approvisionnement en événements, quant à lui, stocke l'historique complet de tout ce qui se passe dans nos Applications. Nous affichons l'état actuel de notre système en "rejouant" et en transformant tous ces événements en modèles de vue :
Event sourcing
L'ensemble du "journal" des événements est appelé flux. Les événements sont associés à un flux primaire qui représente l'entité ou le processus auquel il appartient (client, commande, expédition, etc.).
Sans entrer dans les détails et nous écarter du sujet, nous allons modéliser notre flux pour représenter une conversation téléphonique individuelle, ce que l'API de Vonage rend très facile.
Configuration de la base de données
Nous devons d'abord configurer notre base de données PostgreSQL. L'outil le plus simple pour ce faire est pgAdmin.
Après le téléchargement et l'installation, lancez pgAdmin.
Créer une nouvelle base de données appelée call_audit.
Create database
Ensuite, nous allons créer des informations d'identification pour que notre application .NET puisse communiquer avec la nouvelle base de données. Cliquez avec le bouton droit de la souris sur Login/Rôles de groupe > Créer > Rôle de connexion/de groupe.
Create user
Nom de l'utilisateur call_audit.
Cliquez sur les onglets en haut de la fenêtre modale et cliquez sur Définition. Tapez call_audit comme mot de passe.
Enfin, cliquez sur l'onglet Privilèges et activez Peut-on se connecter ? et Superutilisateur.
Note : N'activez pas "Superuser" pour la production ! Nous ne construisons qu'une application de démonstration.
Création de notre application .NET
Commençons à construire l'application web .NET qui gérera et affichera nos conversations téléphoniques !
Créer une nouvelle application
Pour créer une nouvelle application web .NET Core, exécutez la commande suivante dotnet new mvc -n CallAudit --no-https dans votre terminal.
Ensuite, exécutez ce qui suit pour installer quelques paquets dont vous aurez besoin :
À propos de Marten
Nous allons utiliser la bibliothèque bibliothèque Marten .NET pour nous doter de super-pouvoirs en matière de recherche d'événements.
Marten nous permet d'utiliser facilement Postgres en tant que base de données documentaire et magasin d'événements. Il s'occupera également de la mise à jour de nos modèles de vue côté lecture chaque fois que de nouveaux événements seront ajoutés à nos flux d'événements.
Configuration
Dans votre fichier Startup.cs remplacez la méthode ConfigureServices par la méthode suivante :
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddNewtonsoftJson();
services.AddMarten(options =>
{
options.Connection("Server=127.0.0.1;Port=5432;Database=call_audit;Username=call_audit;Password=call_audit");
options.AutoCreateSchemaObjects = AutoCreate.All;
});
}
Créer nos événements
Nous devrons créer des objets C# pour représenter les événements du domaine dans notre application. Bien que ces événements soient très similaires à ceux que nous recevons de l'API de Vonage (voir plus loin), nous voulons être spécifiques quant à ce que nous enregistrons dans notre magasin d'événements et avoir un contrôle total.
Remarque : lorsque vous créez des systèmes basés sur des événements, vous ne devez pas stocker des "événements" provenant de systèmes externes. Vous devez toujours les convertir en événements spécifiques à votre domaine/système. Ainsi, le cœur de votre système reste découplé et n'est pas affecté par les changements apportés à ces événements ou systèmes externes.
Créez ensuite un nouveau répertoire CallAudit/Events. Voici les événements à créer :
public class CallAnswered
{
public Guid Id { get; set; }
public Guid ConversationId { get; set; }
}
public class CallStarted
{
public Guid Id { get; set; }
public Guid ConversationId { get; set; }
public string From { get; set; }
public string To { get; set; }
}
public class CallCompleted
{
public Guid Id { get; set; }
public Guid ConversationId { get; set; }
public DateTimeOffset? StartTime { get; set; }
public DateTimeOffset? EndTime { get; set; }
public int Duration { get; set; }
} Création de notre côté lecture
Chaque fois que nous ajoutons des événements à nos flux d'événements, Marten crée/met automatiquement à jour un document dans Postgres en tant que version mise en cache de l'état actuel de notre flux. A un autre endroit de notre application, nous pourrons interroger la base de données de documents en utilisant les capacités de stockage de documents de Marten et afficher notre état actuel.
Le type Conversation est le modèle de vue qui représentera l'état actuel de notre flux mis en cache. Voir la documentation de documentation Marten pour en savoir plus.
Créons la classe Conversation dans le dossier CallAudit/Projections:
public class Conversation
{
private Conversation() { }
public Guid Id { get; set; }
public string From { get; set; }
public string To { get; set; }
public bool Answered { get; set; } = false;
public DateTime? EndedAt { get; set; }
public int? Duration { get; set; }
public void Apply(CallStarted started)
{
this.From = started.From;
this.To = started.To;
}
public void Apply(CallAnswered answered)
{
this.Answered = true;
}
public void Apply(CallCompleted completed)
{
this.EndedAt = completed.EndTime;
this.Duration = completed.Duration;
}
}Enfin, nous devons indiquer à Marten que nous voulons que notre flux crée/mette à jour la projection Conversation automatiquement pour nous.
A l'intérieur Startup.cs dans la méthode ConfigureServices() ajouter ce qui suit :
services.AddMarten(options =>
{
options.Connection("Server=127.0.0.1;Port=5432;Database=call_audit;Integrated Security=true;");
options.AutoCreateSchemaObjects = AutoCreate.All;
/***************
* Add this one!
***************/
options.Events.InlineProjections.AggregateStreamsWith<Conversation>();
}); Gestionnaire de commandes
Dans le cadre de l'approvisionnement en événements, les événements sont généralement créés par des commandes. Nous allons créer une classe C# qui exposera les trois différents gestionnaires que nous voulons déclencher lors de la réception d'un message provenant de l'API de suivi des appels de Vonage.
Nous allons créer une classe CallAudit/Handlers/CallAuditHandlers:
using System;
using System.Threading.Tasks;
using CallAudit.Events;
using Marten;
using Vonage.Voice.EventWebhooks;
namespace CallAudit.Handlers
{
public class CallAuditHandlers
{
private IDocumentSession _session;
public CallAuditHandlers(IDocumentSession session)
{
this._session = session;
}
public async Task Handle(CallStatusEvent @event)
{
switch (@event)
{
case Started started:
this.HandleCallStarted(started);
break;
case Answered answered:
this.HandleCallAnswered(answered);
break;
case Completed completed:
this.HandleCallCompleted(completed);
break;
}
await this._session.SaveChangesAsync();
}
private void HandleCallStarted(Started started)
{
var eventToStore = new CallStarted
{
ConversationId = Guid.Parse(FormatUuid(started.ConversationUuid)),
From = started.From,
To = started.To
};
// Create an individual stream per phone conversation.
this._session.Events.Append(eventToStore.ConversationId, eventToStore);
}
private void HandleCallAnswered(Answered answered)
{
var eventToStore = new CallAnswered()
{
ConversationId = Guid.Parse(FormatUuid(answered.ConversationUuid))
};
this._session.Events.Append(eventToStore.ConversationId, eventToStore);
}
private void HandleCallCompleted(Completed completed)
{
var eventToStore = new CallCompleted()
{
ConversationId = Guid.Parse(FormatUuid(completed.ConversationUuid)),
StartTime = completed.StartTime,
EndTime = completed.EndTime,
Duration = int.Parse(completed.Duration)
};
this._session.Events.Append(eventToStore.ConversationId, eventToStore);
}
private static string FormatUuid(string conversationUuid)
{
return conversationUuid.Replace("CON-", string.Empty);
}
}
} Interface utilisateur pour afficher les conversations téléphoniques
À un moment donné, vous voudrez consulter les données de votre système. Remplacez le contenu de Views/Home/Index.cshtml par ce qui suit :
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<table class="table table-bordered">
<thead class="table-dark">
<tr>
<th>From</th>
<th>To</th>
<th>Answered</th>
</tr>
</thead>
<tbody>
@foreach (var convo in Model.Conversations)
{
<tr>
<td>@convo.From</td>
<td>@convo.To</td>
<td>@(convo.Answered ? "yes" : "no")</td>
</tr>
}
</tbody>
</table>
</div>
Créez ensuite une classe C# à l'adresse Models/IndexModel:
using System.Collections.Generic;
using CallAudit.Projections;
namespace CallAudit.Models
{
public class IndexModel
{
public IEnumerable<Conversation> Conversations { get; set; }
}
}Ensuite, remplacez le contrôleur d'origine à Controllers/HomeController.cs par ce qui suit :
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using CallAudit.Models;
using CallAudit.Projections;
using Marten;
namespace CallAudit.Controllers
{
public class HomeController : Controller
{
private readonly IDocumentStore _store;
public HomeController(IDocumentStore store)
{
this._store = store;
}
public async Task<IActionResult> Index()
{
using var session = this._store.OpenSession();
var model = new IndexModel();
model.Conversations = await session.Query<Conversation>()
.ToListAsync();
return this.View(model);
}
}
}Cette page affiche toutes les conversations téléphoniques auditées dans notre système.
Point d'arrivée des webhooks
La dernière partie de notre application web consiste à créer les points d'extrémité qui seront utilisés par Vonage pour envoyer des événements de suivi téléphonique.
Créer un contrôleur MVC dans le dossier Controllers nommé PhoneCallWebhooksController.
Voici le code qui doit y figurer :
using System.IO;
using System.Threading.Tasks;
using CallAudit.Handlers;
using Marten;
using Microsoft.AspNetCore.Mvc;
using Vonage.Voice.EventWebhooks;
using Vonage.Voice.Nccos;
namespace CallAudit.Controllers
{
public class PhoneCallWebhooksController : Controller
{
private readonly IDocumentStore _store;
public PhoneCallWebhooksController(IDocumentStore store)
{
this._store = store;
}
[HttpGet("/track-call")]
public string TrackCall()
{
var talkAction = new TalkAction
{
Text = "This call will be tracked and stored using event sourcing."
};
var ncco = new Ncco(talkAction);
return ncco.ToString();
}
[HttpPost("/event")]
public async Task<IActionResult> Event()
{
// Read the incoming json and load it as the
// proper C# type it represents ("Started", "Answered", etc.)
var json = await new StreamReader(this.Request.Body).ReadToEndAsync();
var @event = (CallStatusEvent) EventBase.ParseEvent(json);
using var session = this._store.OpenSession();
await new CallAuditHandlers(session).Handle(@event);
return this.Ok();
}
}
} Configurer notre application de suivi du temps Vonage
Commençons par l'API Vonage et le suivi des appels en temps réel !
N'oubliez pas d'avoir votre Account Vonage et votre CLI prêts.
Utilisation de ngrok
Pour s'assurer que l'API de Vonage peut se connecter aux webhooks que nous avons créés, nous avons besoin d'une URL publique pour héberger notre site.
Après avoir installé ngrok (un prérequis), vous devez configurer votre jeton d'authentification.
Visiter ce lien et exécutez la commande pour configurer votre token.
Ensuite, exécutez ce qui suit dans un terminal et laissez-le ouvert et en cours d'exécution :
ngrok http http://localhost:5000
Création d'une application Vonage
En utilisant l'URL publique que ngrok vous a donnée, ouvrez un terminal et exécutez la commande suivante (en remplissant les URL) :
nexmo app:create --keyfile private.key callaudit http://YOUR_URL.com/track-call http://YOUR_URL.com/event.
Vous devriez voir "Saved application id : XXXXX". Copiez l'identifiant de l'application, vous en aurez besoin.
Ensuite, visitez cette page pour voir vos numéros Vonage disponibles. Si vous avez un essai gratuit, vous devriez déjà avoir un numéro disponible.
Prenez ce numéro de téléphone et l'identifiant de l'application, puis exécutez ce qui suit :
nexmo link:app [number] [application ID]
Vous devriez obtenir le message "numéro mis à jour".
Exécutons-le !
D'accord, essayons cela !
Depuis le répertoire racine de votre application .NET Core dans un terminal, exécutez dotnet run.
Avec ngrok toujours en cours d'exécution, essayez d'appeler le numéro de téléphone de Vonage que vous avez lié à cette application.
Une fois l'appel terminé, accédez à http://localhost:5000/ dans votre navigateur web et vous devriez voir la liste de vos conversations.
Conclusion
Vous avez maintenant construit un système d'Audit des appels téléphoniques qui utilise la recherche d'événements ! Essayez de jouer avec les autres méthodes de Marten pour interroger les flux d'événements, par exemple transformer un événement directement en un document de base de données côté lecture pour voir de quelles autres façons vous pouvez visualiser vos conversations téléphoniques produites par l'API de Vonage !
Partager:
James is a Microsoft MVP with a background in fintech & insurance industries building web and mobile applications. He's the author of Refactoring TypeScript and the creator of some open-source tools for .NET called Coravel. He lives in eastern Canada and is a 10-minute drive from the highest tides in the world!
