
Teilen Sie:
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 Telefonanrufe mit Event Sourcing in .NET
Lesedauer: 7 Minuten
Ereignisse sind überall! Die Welt der Softwareentwicklung hat die Vorteile der Modellierung unserer Geschäftsprozesse und Anwendungslogik als Ereignisprotokoll erkannt. Event Sourcing erfreut sich zunehmender Beliebtheit als eine Möglichkeit, Systeme zu erstellen, die vor Datenverlust schützen, komplexe Geschäftsszenarien klarer modellieren und flexibel erweitert werden können.
Wenn wir mit Fachleuten sprechen, verwenden wir natürlich Ereignisse, um Geschäftsszenarien zu beschreiben. Hier ist zum Beispiel eine kurze Diskussion, die den Kontext dieses Tutorials umrahmt:
Geschäftsexperte: "Wenn ein Kunde einen Telefonanruf tätigt, wollen wir auditieren, ob der Anruf angenommen wurde und wann der Anruf beendet wurde."
Entwickler: "Ok. Wir müssen also verfolgen, wann Telefonanrufe begonnen, beantwortet und abgeschlossen werden?"
In dieser kurzen Diskussion zur Anforderungserfassung haben wir drei verschiedene Ereignisse aufgedeckt, die wir erfassen müssen: Anruf begonnen, Anruf angenommen und Anruf beendet.
Zum Glück für uns, Vonage über eine fantastische API zur Verfolgung von Anrufen!
Wir werden die Vonage-API verwenden und eine .NET Core-Anwendung erstellen, die diese Informationen mithilfe von Event Sourcing speichert und anzeigt.
Code
Wenn Sie fortfahren möchten, können Sie sich den Code für dieses Lernprogramm auf GitHub einsehen.
Vonage API-Konto
Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.
Voraussetzungen
Für die ersten Schritte benötigen Sie:
Eine IDE für die .NET-Entwicklung wie Visual Studio, Visual Studio Code oder JetBrains Rider
Die neueste Version von .NET / .NET Core installiert
Eine lokale Postgres
ngrok heruntergeladen und installiert
Grundlagen der Veranstaltungsbeschaffung
Event Sourcing ist etwas ganz anderes als das, was wir in der Softwarebranche gewohnt sind.
Normalerweise neigen wir dazu, den aktuellen Zustand unseres Systems in Datenbanktabellen oder Dokumenten zu speichern. Alle historischen Daten werden separat gespeichert.
Event Sourcing hingegen speichert die gesamte Historie von allem, was in unseren Applications passiert. Wir zeigen den aktuellen Zustand unseres Systems an, indem wir all diese Ereignisse "abspielen" und in View-Modelle umwandeln:
Event sourcing
Das gesamte "Protokoll" der Ereignisse wird als Stream bezeichnet. Ereignisse sind mit einem primären Stream verknüpft, der die Entität oder den Prozess darstellt, zu dem sie gehören - ein Kunde, ein Auftrag, eine Lieferung usw.
Ohne ins Detail zu gehen und vom Thema abzuschweifen, werden wir unseren Stream so modellieren, dass er ein einzelnes Telefongespräch darstellt, was mit der Vonage-API sehr einfach ist.
Datenbank-Konfiguration
Zunächst müssen wir unsere PostgreSQL-Datenbank konfigurieren. Das einfachste Werkzeug dafür ist pgAdmin.
Nach dem Herunterladen und der Installation führen Sie pgAdmin aus.
Erstellen Sie eine neue Datenbank mit dem Namen call_audit.
Create database
Als nächstes erstellen wir einige Anmeldeinformationen, damit unsere .NET-Anwendung mit der neuen Datenbank kommunizieren kann. Rechtsklick Anmeldung/Gruppenrollen > Erstellen > Login-/Gruppen-Rolle.
Create user
Benennen Sie Ihren Benutzer call_audit.
Klicken Sie auf die Registerkarten am oberen Rand des modalen Fensters und dann auf Definition. Geben Sie call_audit als Kennwort ein.
Klicken Sie schließlich auf die Registerkarte Privilegien und aktivieren Sie Kann ich mich anmelden? und Superuser.
Hinweis: Aktivieren Sie "Superuser" nicht für die Produktion! Wir bauen nur eine Demo-Anwendung.
Aufbau unserer .NET-Anwendung
Beginnen wir mit der Erstellung der .NET-Webanwendung, die unsere Telefongespräche verarbeiten und anzeigen wird!
Eine neue Anwendung erstellen
Um eine neue .NET Core-Webanwendung zu erstellen, führen Sie dotnet new mvc -n CallAudit --no-https in Ihrem Terminal aus.
Führen Sie als Nächstes das folgende Programm aus, um einige Pakete zu installieren, die Sie benötigen:
Über Marten
Wir werden die Marten .NET-Bibliothek um uns Superkräfte für die Ereignisbeschaffung zu geben.
Marten gibt uns die Möglichkeit, Postgres einfach als Dokumenten-DB und Ereignisspeicher zu verwenden. Es wird auch die Aktualisierung unserer Ansichtsmodelle auf der Leseseite übernehmen, wenn neue Ereignisse an unsere Ereignisströme angehängt werden.
Konfiguration
In Ihrer Startup.cs Datei, ersetzen Sie die ConfigureServices Methode durch die folgende:
// 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;
});
}
Erstellen unserer Veranstaltungen
Wir müssen einige C#-Objekte erstellen, um die Domänenereignisse in unserer Anwendung darzustellen. Diese Ereignisse werden den Ereignissen, die wir von der Vonage-API (später) erhalten, sehr ähnlich sein, aber wir wollen genau wissen, was wir in unserem Ereignisspeicher speichern und die volle Kontrolle haben.
Hinweis: Wenn Sie ereignisgesteuerte Systeme aufbauen, sollten Sie keine "Ereignisse" speichern, die von externen Systemen stammen. Sie sollten sie immer in Ereignisse umwandeln, die für Ihren Bereich/System spezifisch sind. Dadurch bleibt der Kern Ihres Systems entkoppelt und von Änderungen an diesen externen Ereignissen oder Systemen unbeeinflusst.
Als nächstes erstellen Sie ein neues Verzeichnis CallAudit/Events. Hier sind die zu erstellenden Ereignisse:
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; }
} Unsere Leseseite erstellen
Jedes Mal, wenn wir Ereignisse an unsere Ereignisströme anhängen, erstellt/aktualisiert Marten automatisch ein Dokument in Postgres als gecachte Version des aktuellen Zustands unseres Streams. An anderer Stelle in unserer Anwendung können wir die Dokumenten-DB mit Martens Dokumentenspeicher-Funktionen abfragen und unseren aktuellen Status anzeigen.
Der Conversation Typ ist das Ansichtsmodell, das den zwischengespeicherten aktuellen Zustand unseres Streams darstellt. Siehe die Marten-Dokumentation für weitere Informationen dazu.
Erstellen wir die Conversation Klasse unter dem Ordner 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;
}
}Schließlich müssen wir Marten mitteilen, dass unser Stream die Projektion automatisch erstellen/aktualisieren soll. Conversation Projektion automatisch für uns erstellen/aktualisieren soll.
Unter Startup.cs in der ConfigureServices() Methode fügen Sie Folgendes hinzu:
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>();
}); Befehlshandler
Beim Event Sourcing werden Ereignisse in der Regel durch Befehle erzeugt. Wir erstellen eine C#-Klasse, die die drei verschiedenen Handler bereitstellt, die wir beim Empfang einer Nachricht von der Vonage-API zur Anrufverfolgung auslösen möchten.
Wir werden eine Klasse erstellen 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);
}
}
} UI zur Anzeige von Telefongesprächen
Irgendwann werden Sie die Daten aus Ihrem System einsehen wollen. Ersetzen Sie den Inhalt von Views/Home/Index.cshtml durch den folgenden:
@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>
Als nächstes erstellen Sie eine C#-Klasse unter Models/IndexModel:
using System.Collections.Generic;
using CallAudit.Projections;
namespace CallAudit.Models
{
public class IndexModel
{
public IEnumerable<Conversation> Conversations { get; set; }
}
}Als Nächstes ersetzen Sie die Haussteuerung unter Controllers/HomeController.cs durch den folgenden:
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);
}
}
}Auf dieser Seite werden alle in unserem System geprüften Telefongespräche angezeigt.
Webhooks Endpunkt
Der letzte Teil unserer Webanwendung besteht darin, die Endpunkte zu erstellen, die von Vonage zum Senden von Telefonverfolgungsereignissen verwendet werden sollen.
Erstellen Sie einen MVC-Controller unter dem Controllers Ordner mit dem Namen PhoneCallWebhooksController.
Hier ist der Code, der darin enthalten sein sollte:
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();
}
}
} Konfigurieren Sie unsere Vonage Zeiterfassungsanwendung
Lassen Sie uns mit der Vonage API und der Anrufverfolgung in Echtzeit beginnen!
Denken Sie einfach daran, dass Sie Ihr Vonage Account und CLI bereithalten.
Verwendung von ngrok
Um sicherzustellen, dass die Vonage API eine Verbindung zu den von uns erstellten Webhooks herstellen kann, benötigen wir eine öffentliche URL, unter der wir unsere Website hosten.
Nachdem Sie ngrok installiert haben (eine Voraussetzung), müssen Sie Ihr Auth-Token konfigurieren.
Besuchen Sie diesen Link und führen Sie den Befehl zur Konfiguration Ihres Tokens aus.
Führen Sie als Nächstes den folgenden Befehl in einem Terminal aus und lassen Sie ihn geöffnet und ausgeführt:
ngrok http http://localhost:5000
Erstellen einer Vonage-Anwendung
Verwenden Sie die öffentliche URL, die Sie von ngrok erhalten haben, öffnen Sie ein Terminal und führen Sie den folgenden Befehl aus (geben Sie die URLs ein):
nexmo app:create --keyfile private.key callaudit http://YOUR_URL.com/track-call http://YOUR_URL.com/event.
Sie sollten die Meldung "Saved application id: XXXXX". Kopieren Sie diese Anwendungs-ID - Sie werden sie brauchen.
Besuchen Sie dann diese Seite, um Ihre verfügbaren Vonage Numbers zu sehen. Wenn Sie eine kostenlose Testversion haben, sollte bereits eine Nummer verfügbar sein.
Nehmen Sie diese Telefonnummer und die Anwendungs-ID und führen Sie Folgendes aus:
nexmo link:app [number] [application ID]
Sie sollten die Meldung "Nummer aktualisiert" erhalten.
Los geht's!
Also gut, probieren wir es aus!
Führen Sie im Stammverzeichnis Ihrer .NET Core-Anwendung in einem Terminal Folgendes aus dotnet run.
Rufen Sie bei laufendem ngrok die Vonage-Telefonnummer an, die Sie mit dieser Anwendung verknüpft haben.
Nachdem Sie das Gespräch beendet haben, navigieren Sie in Ihrem Webbrowser zu http://localhost:5000/. Dort sollten Ihre Gespräche aufgelistet sein.
Schlussfolgerung
Jetzt haben Sie ein System zur Überprüfung von Telefongesprächen aufgebaut, das Event Sourcing verwendet! Versuchen Sie, mit Martens anderen Möglichkeiten zur Abfrage der Ereignisströme zu spielen, wie die direkte Umwandlung eines Ereignisses in ein Datenbankdokument auf der Leseseite um zu sehen, welche anderen Möglichkeiten Sie haben, Ihre von der Vonage-API erzeugten Telefongespräche einzusehen!
Teilen Sie:
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!
