September 2016

Band 31, Nummer 9

Dieser Artikel wurde maschinell übersetzt.

Cutting Edge: Nachrichtenbasierte Geschäftslogik in der Praxis

Durch Dino Esposito | September 2016

Dino EspositoEs ist allgemein bekannt, um effiziente Unternehmenssoftware zu erstellen, müssen Sie sorgfältig um Geschäftsereignisse zu modellieren und täuschend Spiegeln von Geschäftsprozessen.

Einige vor 20 Jahren im Flussdiagramm verwendet, um das ideale Tool große Teile einer Anwendung dargestellt werden. Im Laufe der Jahre wuchs die Größe von Flussdiagrammen so groß, Handles und Techniker an anderer Stelle suchen, finden Sie ein geeignetes Modell werden die Komplexität des Unternehmens kann nicht ausgeführt werden. Umfassende, vollständig Unified Modeling Language (UML)-Diagramme erschien die beste Wahl, starten aus dem Klassendiagramm, das verwendet wird, um das Domänenmodell zu vermitteln. Die schien die Essenz des Domain-Driven Design (DDD) und für viele Projekte arbeiteten, jedoch auch Fehler Gründen schlechte Fähigkeiten, fehlende Kommunikation, die geeignete Reihe von Technologien und vor allem nicht genügend Kenntnisse über die Funktionsweise des Unternehmens verarbeitet werden.

Ein neuer und moderner Ansatz für Softwareentwicklung ist Geschäftsereignisse und solche Ereignisse sammeln Sie Anforderungen, und erstellen ein besseres Verständnis für die Funktionsweise des Systems verwenden. Anstatt zu künstlich erstellen ein neues Modell für das beobachtete Unternehmen, Streben Sie also Spiegelung mithilfe von Code die gleiche Geschäftsprozesse, Projektbeteiligten und Analysten beschreiben, und dies also Endbenutzer mit den Verfahren vertraut sind.

In diesem Artikel stelle ich eine praktische Veranschaulichung dieses Ansatzes bereit, von Ausdrücken relevanten Geschäftsprozesse durch Softwareentitäten namens "Sagas." Ad-hoc-Ansichtsmodelle verwende ich außerdem jeder Bildschirm füllen Sie mit dem Benutzer vertraut sind. Abschließend werde ich die Schritte des jeweiligen Anwendung Workflows in eine Kombination von Nachrichten (Befehle und Ereignisse), die von den beteiligten Parteien ausgetauscht aufteilen. Sie werden überrascht sein, wie einfacher wird es Ausdrücken sogar komplexe Teile der Geschäftslogik und, mehr als alles andere, wie schnell und zuverlässig es kann werden ändern die Implementierung, die Variable Geschäftsregeln synchron bleiben.

Ich möchte eine Reihe neuer Software-Entitäten wie z. B. Sagas, Befehle, Ereignisse, Busse und Anwendungsdienste vorstellen. Um das Rad für jede neue Anwendung nicht neu zu erfinden, und viele sich wiederholende Codes zu speichern – ich werde auch skizzieren, die Merkmale eines Frameworks, mit denen Sie diese Art von Ereignis-basierte Architektur implementieren können.

Alternative Verfahren zum Erreichen der gleiche

Zusammengestellt, die Strategien, die "spiegeln die Geschäftsprozesse" und "Spiegelung des Benutzers Prozeduren" für eine andere Art der Softwareentwicklung besteht. Die allgemeine Methodik wird oben nach unten anstatt von unten nach oben. Eine geeignete Möglichkeit könnte darin bestehen, starten Sie das System von der Darstellungsschicht, die durch die Modelle anzeigen, die hinter dem benutzerseitige Bildschirme und Assistenten dient in erster Linie gesteuert wird. Insgesamt Benutzer Informationen eingeben Bildschirme, mit denen bildet des Ansichtsmodell des Bildschirms, und für alle nachfolgenden Business Aktionen gegen das System eingegeben wird.

Wenn Benutzer mit der Benutzeroberfläche arbeiten, werden Befehle für die Anwendungsebene übertragen – die oberste Gate Geschäftslogik verantwortlich für das Erfassen von Benutzereingaben und Rückgabe einer gewünschten Ansichten. Die Anwendungsebene wird die Orchestrator Logik hinter die Anwendungsfälle der Anwendung angezeigt. In der Regel wird die Anwendungsschicht als eine Reihe von Diensten implementiert, daher eine Klassenbibliothek mit Methoden, die 1: 1 mit Benutzeraktionen werden aufgefüllt definitiv den Auftrag ausgeführt wird. Im Kontext einer ASP.NET MVC-Anwendung kann eine Ebene Anwendungsklasse Controllerklassen 1: 1 sein und jeder Controllermethode, die einen Geschäftsvorgang löst am Ende einen Aufruf in eine bestimmte Anwendung Layer-Methode auf eine 1: 1-Siehe Abbildung 1.

Anzeigen des Modells und Anwendungsebene
Abbildung 1 Ansichtsmodell und Anwendungsebene

Als Nächstes zeige ich Ihnen wie eine normale Business-Aktion, z. B. Registrieren einer Rechnung eine nachrichtenbasierte Ansatz zu schreiben.

Präsentation und zurück

Als Referenz, verwende ich einen Auszug der beispielanwendung, die Andrea Saltarello und ich ursprünglich als Begleitcode für unsere Microsoft Press-Architektur-Buch geschrieben habe "Microsoft .NET – Architecting Applications für Unternehmen, 2nd Edition" (2014). Im vergangenen Jahr, der Code erheblich weiterentwickelt, und finden Sie unter bit.ly/29Nf2aX MERP im Ordner.

MERP ist eine ASP.NET MVC-Anwendung, die in zwei Bereichen formuliert. Jede davor eine Kontextgrenze in der Fachsprache DDD ist, und jede betrachtet werden kann, mehr generisch, ein Microservice. In ASP.NET MVC ist ein Bereich eine eindeutige Auflistung von Controllern, Modelle und Razor-Ansichten. Anschließend müssen Sie z. B. eine InvoiceController-Klasse mit einer Methode Problem. Realistisch gesehen, ist die Problem-Aktion durch unterschiedliche GET und POST-Methoden implementiert, siehe Abbildung 2.

Abbildung 2 GET und POST-Methoden für eine Controlleraktion

[HttpGet]
public ActionResult Issue()
{
  var model = WorkerServices.GetIssueViewModel();
  return View(model);
}
[HttpPost]
public ActionResult Issue(IssueViewModel model)
{
  if (!this.ModelState.IsValid)
  {
    return View(model);
  }
  WorkerServices.Issue(model);
  return Redirect("/Accountancy/");
}

Das Element WorkerServices ist der Einstiegspunkt in der Anwendungsschicht und ein Verweis in der Controllerklasse über Abhängigkeitsinjektion eingefügt:

public InvoiceController(InvoiceControllerWorkerServices workerServices)
{
  if(workerServices == null)
    throw new ArgumentNullException(nameof(workerServices));
  WorkerServices = workerServices;
}

Die Struktur der GET-Methode ist ziemlich einfach: Die Anwendungsschicht Ruft das Ansichtsmodell, das an den Bildschirm des Benutzers zum Auslösen der Aktion "Problem" erforderlichen Daten enthält. Die Daten, die die Worker-Dienst gibt als übergeben werden der Razor-Ansicht.

Die POST-Methode folgt eine CQRS-Logik und führt den Befehl aus, der die Rechnung, daher ändern den Zustand des Systems ausstellt. Der Befehl nicht unbedingt alle direktes Feedback für den Benutzer generieren, aber eine Umleitung tritt auf, um den Befehl physisch von aufeinander folgenden Abfragen zu trennen, die tatsächlich die Benutzeroberfläche aktualisiert wird.

Teilen Sie die erforderliche Logik

In einem Implementierungsszenario mit klassischen die Dienstmethode Worker Eingabedaten erfasst und einen sequenziellen hartcodiert Workflow auslösen. Am Ende des Workflows wird die Dienstmethode Ergebnisse übernehmen und Verpacken in eine Datenstruktur zum oberen Ebenen des Codes zurückgegeben werden kann.

Der Workflow ist in der Regel eine monolithische Prozedur versucht der bedingten Verzweigungen, Schleifen und was dient, die erforderliche Logik auszudrücken. Der Workflow wird wahrscheinlich ein Flussdiagramm Domänenexperten gezeichnet haben, aber am Ende des Tages herausstellt ein Software-Artefakt sein, in dem der Fluss der Arbeit durch die Konstrukte und Komplexität der Programmiersprache oder ad-hoc-Workflow-Frameworks vereinfacht wird. Selbst eine kleine Änderung vornehmen eventuell erhebliche Regressionstests Effekte. Während der Komponententests vorhanden sein, um Regression zu steuern, sind sie immer noch häufig als eine zusätzliche Belastung, zusätzliche Kosten und Aufwand erfordern angesehen.

Was geschieht, stattdessen, wenn Sie die Geschäftsprozesse mithilfe von Befehlen orchestrieren.

Übergeben von Befehlen an den Bus

Software-basierten ausgedrückt ist der Befehl ein Datenpaket, d. h. eine POCO-Klasse mit nur Eigenschaften und keine Methoden. Abstrakt ausgedrückt kann ein Befehl stattdessen als imperative Nachricht an das System übermittelt, einige Aufgaben durchgeführt beschrieben werden. Hier ist, wie Sie eine Aufgabe auslösen können, durch Übergeben eines Befehls an das System:

public void Issue(IssueViewModel model)
{
  var command = new IssueInvoiceCommand(
    model.Date,
    model.Amount,
    ...
  );
  Bus.Send(command);
}

Das Ansichtsmodell enthält die Eingabedaten für das System zu verarbeiten. Die Bindung der Ebene von ASP.NET MVC funktioniert bereits als Mapping zwischen gesendeten Daten und Befehl. Es käme nicht überraschen, wenn Sie vorhaben wurden von der Command-Klasse als Ziel der ASP.NET MVC-modellbindung verwenden. In der Praxis erfolgt jedoch modellbindung in den Teil der Darstellungsschicht Controllerkontext ist ein Befehl eine Nachricht, die Entscheidung der Ebenen der Anwendung oder sogar zu einem späteren Zeitpunkt folgt. Zugegeben, im vorherigen Codeausschnitt nähert sich der Command-Klasse wird eine Kopie der Ansichtsmodell-Klasse allerdings hauptsächlich aufgrund der extremen Einfachheit des Beispiels.

Nachdem Sie eine Befehlsinstanz bereit haben, wird im nächste Schritt an das System übermitteln. In einem klassischen Softwareentwurf verfügt über einen Befehl ein, der sie von Anfang bis Ende übernimmt Executor. Der besteht darin, dass jeder Befehl vom System abgelehnt kann und noch wichtiger ist, den Effekt des Befehls sowie die Liste der tatsächlichen Handler unterscheidet sich möglicherweise je nach Zustand des Systems.

Ein Bus ist ein Veröffentlichen/Abonnieren-Mechanismus, der hauptsächlich für die Suche nach einem Handler für den Befehl verantwortlich ist. Die Auswirkung der Handler kann eine einzelne Aktion oder, wahrscheinlicher ist, kann der Befehl eine Saga auslösen. Eine Saga ist eine Instanz eines bekannten Geschäftsprozesses, die in der Regel mehrere Befehle und Benachrichtigungen für Sagas und andere Ereignisse zu reagieren, umfasst. 

Der Bus ist ein zentrales Element in einer nachrichtenbasierten Architektur. Zumindest ist die Busschnittstelle wahrscheinlich eine die folgenden Methoden:

public interface IBus
{
  void Send<T>(T command) where T : Command;
  void RegisterSaga<T>() where T : Saga;
  void RegisterHandler<T>();
}

Zusätzlich zu den Send-Methode verwendet, um Befehle zu veröffentlichen bietet der Bus in der Regel Methoden zum Registrieren von Sagas und. Sagas und sind ähnliche Softwarekomponenten in dem Sinne, dass beide Befehle zu verarbeiten. Ein Handler ist jedoch startet und in einem einzelnen Testlauf abgeschlossen ist, ohne jemals wieder an den Bus. Ein allgemeiner Typ der Handler ist die Denormalizer, d. h. eine Komponente, die eine schreibgeschützte Projektion des aktuellen Zustands eines Aggregats in einer vollständigen CQRS-Architektur speichert. Eine Saga kann mehreren Schritten, der aus mehreren Befehlen und ereignisbenachrichtigungen, die zurück an den Bus für andere Handler und Sagas reagieren abgelegt werden.

Konfigurieren des Bus

Konfiguration des Busses erfolgt während des Starts der Anwendung. Zu diesem Zeitpunkt werden Geschäftsprozesse in Sagas und optional aufgeteilt. Jeder Saga wird vollständig durch eine Starter-Nachricht ("Befehl" oder "Ereignis") und eine Liste von Nachrichten, die nicht verarbeitet werden identifiziert. Abbildung 3 ist ein Beispiel.

Abbildung 3: Konfigurieren des Bus

// Sagas
var bus = new SomeBus();
...
  bus.RegisterSaga<IncomingInvoiceSaga>();
  bus.RegisterSaga<OutgoingInvoiceSaga>();
// Handlers (denormalizers)
bus.RegisterHandler<IncomingInvoiceDenormalizer>();
bus.RegisterHandler<InvoiceDenormalizer>();
bus.RegisterHandler<OutgoingInvoiceDenormalizer>();
// Here’s a skeleton for the saga components above.
public class IncomingInvoiceSaga : Saga,
  IAmStartedBy<RegisterIncomingInvoiceCommand>
{...
}
public class OutgoingInvoiceSaga : Saga,
  IAmStartedBy<IssueInvoiceCommand>
{
  ...
}

Eine Saga-Klasse erbt in der Regel von einer Basisklasse, die Verweise auf Ressourcen bequem-, die Sie wahrscheinlich Pakete möchten Sie z. B. den Ereignisspeicher, den Bus und die zusammengefassten Repository verwenden. Wie bereits erwähnt, wird durch die Starter-Nachricht zusammengefasst, indem Sie die IAmStartedBy < T >-Schnittstelle, wobei T ein Ereignis oder einen Befehl ist, auch eine Saga gekennzeichnet:

public interface IAmStartedBy<T>
{
  void Handle(T message);
}

Wie Sie sehen, zählt die IAmStartedBy < T >-Schnittstelle eine einzelne Methode – behandeln – die ruft einer Instanz des Typs T. Der Text der Handle-Methode enthält den eigentlichen Code die Rechnung und mehr erfassen, um die Geschäftsaufgabe auszuführen. Am Ende des Vorgangs möglicherweise der Benachrichtigung anderer Sagas oder Handler von Was passiert ist erforderlich. Insbesondere sollten Sie zum Auslösen eines Ereignisses an andere Benutzer zu informieren, dass die Rechnung erfolgreich registriert wurde oder ob fehlgeschlagen ist und warum. Handler "Fehler" Benachrichtigung werden verantwortlich für Vergütung oder Rollback-Verfahren und für jede Art von Feedback an den Benutzer generiert.

Von Flussdiagrammen, Sagas

Es gibt eine inhärente Ähnlichkeit zwischen Flussdiagramme Domänenexperten möglicherweise verwenden, um den Umriss eines Geschäftsprozesses und wie Sie Sagas im Code definieren können. Auf eine Weise ist die Saga die Implementierung eines Flussdiagramms über eine empfangene Ereignis oder einen Befehl behandeln kann, in dem jeder Block gerendert werden. Eine Saga kann ein langer Prozess und kann sogar werden angehalten und fortgesetzt werden. Denken Sie an, z. B. einen Geschäftsprozess, die einen Genehmigungsschritt umfasst (z. B. nach offline Recherchen für einen Kunden). Die Saga muss gestartet werden, und bei Erreichen die Stufe der Genehmigung sollte ein Ereignis empfangen und Abrufen von der Handler serialisiert. Als Nächstes wird die Saga fortgesetzt werden, wenn Code mit dem Befehl sendet, der die Genehmigung Zeugen beteiligt sind. Wie Sie sehen können, erleichtert die Geschäftslogik in micro Schritte aufgeteilt haben auch erweitern und ändern die Logik mit dem wachsenden Unternehmen synchron bleiben.

Aus Theorie zu Frameworks

Bisher habe ich das Vorhandensein von Komponenten wie Sagas und Bussen angenommen. Wer schreibt den Bus und Dauerhaftigkeit Sagas behandelt? Und was die Definitionen von Schnittstellen und Base Klassen, erwähnt als Teil eines Frameworks?

Wenn Sie den Quellcode betrachten Projekt MERP aus bit.ly/29Nf2aX, sehen Sie, dass Klassen wie der Bus, Saga, einen Befehl und vieles mehr verwendet. In der neuesten Version des Codes stammen jedoch diese Klassen ein paar neue NuGet-Pakete, die MementoFX beinhaltete. Eine typische Anwendung basierend auf dem Framework Erinnerungsstück erfordert des MementoFX-Pakets, die Basisklassen und eine Reihe von Helper-Paketen für den Bus (Memento.Messaging.Postie oder Memento.Messaging.Rebus gegenwärtig) und Persistenz des Ereignisses definiert. Im Moment MERP Quellcode verwendet das eingebettete RavenDB-Modul für die Dauerhaftigkeit und in einem Paket Memento.Persistence.EmbeddedRavenDB wrappt: Auf diese Weise wird der Ereignisspeicher gestartet, sobald der ASP.NET-Prozess aktiv ist.

Mithilfe der MementoFX-Pakete, können Sie den Aufwand beim Erstellen nachrichtenbasierte Geschäftslogik deutlich reduzieren. Lassen Sie mich Ihre Meinung hören!


Dino Espositoist Autor von „Microsoft .NET: Architecting Applications for the Enterprise“ (Microsoft Press, 2014) und „Modern Web Applications with ASP.NET“ (Microsoft Press, 2016). Esposito ist Technical Evangelist für die .NET- und Android-Plattformen bei JetBrains und spricht häufig auf Branchenveranstaltungen weltweit. Auf software2cents.wordpress.com und auf Twitter unter twitter.com/despos lässt er uns wissen, welche Softwarevision er verfolgt.

Unser Dank gilt dem folgenden technischen Experten bei Microsoft für die Durchsicht dieses Artikels: Andrea Saltarello