Dieser Artikel wurde maschinell übersetzt.

Smart Client

Erstellen verteilter Anwendungen mit NHibernate und Rhino Service Bus, Teil 2

Oren Eini

In der Juli 2010-Ausgabe des MSDN Magazine gestartet ich schrittweise durch den Prozess des Erstellens von smart Client-Anwendung für eine Bibliothek Lending. Ich das Projekt Alexandria aufgerufen und beschlossen, NHibernate für Datenzugriff und Rhino Service Bus für zuverlässige Kommunikation mit dem Server zu verwenden.

NHibernate (nhforge.org ) ist ein Framework für objektrelationales Mapping (O/RM) und Rhino Service Bus (github.com/rhino-esb/rhino-esb ) ist eine Implementierung eines open-Source-Service Bus basiert auf Microsoft .NET Framework. Ich zufällig tief in die Entwicklung von beiden dieser Frameworks beteiligt sein so, dass es schien, z. B. die Möglichkeit zum Implementieren ein Projekts mit Technologien, die ich bestens, während gleichzeitig die, bieten ein funktionsfähiges Beispiel für Entwickler, die NHibernate und Rhino Service Bus vertraut machen möchten.

Im vorherigen Artikel behandelt ich die grundlegenden Bausteine der smart Client-Anwendung. Ich entwarf die Back-End zusammen mit der Kommunikationsmodus zwischen smart Client-Anwendung und dem Back-End. Ich berührt auch auf Batchverarbeitung und Zwischenspeicherung, Verwaltung von Transaktionen und die Sitzung NHibernate, verbrauchen und Beantworten von Nachrichten vom Client und wie alles zusammen in den Bootstrapper stammt.

In dieser Ausgabe werden best Practices für das Senden von Daten zwischen den Back-End und dem intelligenten Client Anwendung sowie Muster für verteilte Change Management eingegangen. Sowie die Art und Weise ich die verbleibenden Implementierungsdetails erläutert und einen abgeschlossenen Client für die Anwendung Alexandria wird angezeigt.

Sie können die Beispiellösung von github.com/ayende/alexandria downloaden. Die Lösung besteht aus drei Teilen: Alexandria.Backend hostet den Back-End-Code Alexandria.Client enthält den Front-End-Code; Alexandria.Messages enthält die Nachricht-Definitionen, die gemeinsam genutzt werden.

Niemand Model-Regeln

Einer der häufigsten Fragen Benutzer Fragen Sie beim Schreiben von verteilte Anwendungen: Wie kann ich senden Meine Entitäten an die Clientanwendung und wenden Sie dann die Änderungen auf dem Server festlegen?

Ist dies Ihre Frage, denken Sie wahrscheinlich in einem Modus, ist die Serverseite hauptsächlich ein Datenrepository. Wenn Sie solche Anwendungen erstellen, sind die Auswahlmöglichkeiten von Technologie, die Sie vornehmen können, die diesen Vorgang (z. B. employing WCF RIA-Diensten und WCF Data Services) vereinfachen. Verwenden den Typ der Architektur, die ich bisher skizziert haben, sinnvoll nicht jedoch es wirklich über das Senden von Entitäten in der Leitung zu sprechen. In der Tat verwendet Alexandria drei unterschiedliche Modelle für die gleichen Daten jedes Modell am besten geeignet für verschiedene Teile der Anwendung.

Das Domänenmodell Back-End, der für das Abfragen und transaktionale Verarbeitung verwendet wird, eignet sich für die Verwendung mit NHibernate (und weitere wäre Verfeinerung der Abfragen und transaktionale Verarbeitung Verantwortungsbereiche aufgeteilt). Das Modell der Nachricht stellt Nachrichten bei der Übertragung, darunter einige Konzepte, die eng Domäne Entitäten zugeordnet (BookDTO im Beispielprojekt ist ein Klon Daten des Adressbuchs). In der Clientanwendung ist das Modell anzeigen (z. B. BookModel-Klasse) der XAML-Code gebunden werden und die Behandlung von Benutzerinteraktionen optimiert.

Da auf den ersten Blick viele Gemeinsamkeiten zwischen den drei Modellen (Buch, BookDTO, BookModel) eingesehen werden können, bedeutet die Tatsache, dass Sie verschiedene Zuständigkeiten haben, versucht, alle in einem einzelnen Modell cram umständlich, Heavyweight, 1-Größe-nicht-Anpassung-Personen-Modell erstellen würden. Durch Teilen des Modells entlang der Linien von Verantwortlichkeiten, hergestellt ich die Arbeit erheblich einfacher, da ich jedes Modell unabhängig für eigene Zwecke anpassen können.

Eine grundlegende angeht stehen andere Gründe, ein separates Modell für jede Verwendung erstellen möchten. Ein Objekt ist eine Kombination aus Daten und Verhalten, aber wenn Sie versuchen, ein Objekt über das Netzwerk senden, ist das einzige, was, das Sie senden können, die Daten. Führt zu einigen interessanten Fragen. Wo platzieren führen Geschäftslogik, die auf dem Back-End-Server ausgeführt werden soll? Wenn Sie in den Entitäten eingefügt, was geschieht, wenn Sie diese Logik auf dem Client ausgeführt?

Das Ergebnis dieser Art von Architektur ist keine wirkliche Objekte verwenden. Stattdessen verwenden Sie Datenobjekte – Objekte, die lediglich die Daten enthalten ist – und die Geschäftslogik befindet sich an einer anderen Stelle, wie Prozeduren, die über die Objektdaten ausgeführt. Dies ist bei, frowned, da es zu Streuung der Logik und den Code, die schwieriger ist es führt, um über einen Zeitraum zu verwalten. Unabhängig davon, wie Sie es, betrachten, wenn das Back-End-System für ein einfaches Datenrepository, verschiedene Modelle in verschiedenen Teilen der Anwendung verfügen sollen. Führt natürlich zu einer sehr interessante Frage: Wie wird auf Änderungen reagieren?

Befehle über die Mengen ändern

Zwischen den Vorgängen kann ich Benutzer in der Anwendung Alexandria Bücher an die Warteschlange hinzufügen möchten, Neuanordnen von Büchern, die in der Warteschlange, und wie in Abbildung 1 vollständig aus der Warteschlange entfernen. Diese Vorgänge in der front-End und Back-End wiedergegeben werden müssen.

mögliche Operationen für Bücher des Benutzers in eine Warteschlange einreihen

Abbildung 1 mögliche Operationen für Bücher des Benutzers in eine Warteschlange einreihen

Ich könnte versuchen zu implementieren, indem Sie die Entitäten über die Leitung zu serialisieren und Senden von geänderte Entität zurück an den Server für die Dauerhaftigkeit. In der Tat enthält NHibernate explizite Unterstützung für solchen Szenarios mit session.Merge-Methode.

Let’s angenommen jedoch die folgenden Geschäftsregel: Wenn ein Benutzer ein Buch in Ihrer Warteschlange aus der Liste der Empfehlungen hinzufügt, das Buch aus der Empfehlungen entfernt wird, und eine andere Empfehlung hinzugefügt.

Genommen Sie an, versuchen zu erkennen, dass ein Buch aus der Liste der Empfehlungen für die Warteschlange verwenden einfach den vorherigen und aktuellen Zustand (die Änderung zwischen zwei Zuständen festgelegt) verschoben wurde. Während es möglich ist, ist zu sagen, dass es umständlich wäre, Behandeln einer Understatement.

Ich nenne diese Architekturen Trigger-Oriented Programming. Trigger in einer Datenbank ist was in einem System Änderung Gruppen haben basierend auf wie Code, der hauptsächlich mit Daten umgeht. Um einige Unternehmen aussagekräftigen Semantik zu ermöglichen, müssen Sie die Bedeutung der Änderungen, die von der Änderung festlegen, indem Sie Brute-Force- und Glück zu extrahieren.

Es gibt ein Grund, dass Trigger, die mit der Logik eines Antimusters berücksichtigt werden. Obwohl die entsprechenden einiges (z. B. Replikation oder reine Datenoperationen) versuchen, mithilfe von Triggern Geschäftslogik implementieren mühsam-Prozess, der auf ein System, die schwierig führt zu verwalten ist.

Die meisten Systeme, die einen CRUD--Schnittstelle verfügbar machen und ermöglichen das Schreiben von Geschäftslogik in Methoden, z. B. UpdateCustomer sind Sie Trigger-Oriented Programming als Standard (und in der Regel die einzige Auswahlmöglichkeit) gewähren. Wenn es nicht signifikante Geschäftslogik beteiligten – Wenn das System als Ganzes ist größtenteils über CRUD – bei dieser Architektur ist sinnvoll, aber es ist in den meisten Anwendungen nicht geeignet und nicht empfohlen.

Stattdessen eine explizite Schnittstelle (RemoveBookFromQueue und AddBookToQueue, z. B.) führt ein System, das ist viel einfacher zu verstehen und zu überlegen. Die Möglichkeit zum Austauschen von Informationen auf dieser hohen Ebene ermöglicht eine hervorragende Grad der Freiheit und leicht ändern Sie unterwegs. Nachdem alle Sie Don ’t haben herauszufinden, wobei einige Funktionen im System basiert, auf welche Daten durch diese Funktionalität manipuliert werden. Das System wird genau, wo dies basierend auf seiner Architektur geschieht ausgeschrieben.

Die Implementierung in Alexandria folgt das Prinzip explizite Schnittstellen; Aufrufen dieser Operationen befindet sich im Anwendungsmodell und im Abbildung 2 angezeigt wird. Ich habe mehrere interessante Dinge tun, also let’s behandelt jede dieser Methoden in der Reihenfolge.

Abbildung 2 Hinzufügen eines Buchs in der Benutzer-Warteschlange auf dem Front-End

public void AddToQueue(BookModel book) {
  Recommendations.Remove(book);
  if (Queue.Any(x => x.Id == book.Id) == false) 
    Queue.Add(book);

  bus.Send(
    new AddBookToQueue {
      UserId = userId, BookId = book.Id
    },
    new MyQueueQuery {
      UserId = userId
    },
    new MyRecommendationsQuery {
      UserId = userId
    });
}

Erstens ändern Sie das Anwendungsmodell direkt an die Wünsche der Benutzer sofort widerspiegeln. Ich möglich dies, da hinzufügen, ein Buch, um die Warteschlange der Benutzer einen Vorgang handelt, der garantiert nie fehlschlagen. Ich auch aus der Liste der Empfehlungen entfernen, da es nicht sinnvoll sein, auch ein Element in der Warteschlange für den Benutzer angezeigt werden, in der Liste der Empfehlungen.

Als Nächstes Batchauftrag ich eine Nachricht an den Back-End-Server informiert er das Buch Warteschlange des Benutzers hinzu und mich darüber informiert, welche Warteschlange und Empfehlungen des Benutzers nach dieser Änderung sind. Dies ist ein wichtiges Konzept zum Verständnis.

Die Fähigkeit zum Erstellen von Befehlen und Abfragen in dieser Weise bedeutet, dass Sie Don ’t besondere Maßnahmen ergreifen, wie AddBookToQueue Befehle, um die geänderten Daten an dem Benutzer zu erhalten. Stattdessen front-End können Sie als Teil der gleichen Nachricht Stapel anfordern und vorhandene Funktionalität können Sie verwenden, um diese Daten abzurufen.

Es gibt zwei Gründe, die die Daten aus der Back-End-Server beantragen, obwohl ich die Änderungen im Speicher. Der Back-End-Server möglicherweise zuerst zusätzlichen Logik (z. B. neue Empfehlungen für diesen Benutzer suchen) ausführen, der Änderungen, die Sie auf dem Front-End-Seite führen zu kennen. Zweitens wird die Antwort vom Back-End-Server den Cache mit dem aktuellen Status aktualisieren.

Getrennte lokal Zustandsverwaltung

Möglicherweise haben Sie ein Problem im Abbildung 2 in Bezug auf die nicht verbundene Arbeit bemerkt. Ich die Änderung im Arbeitsspeicher, aber bis ich eine Antwort vom Server wieder abrufen, ist nicht die zwischengespeicherten Daten damit diese Änderungen wiedergegeben werden. Wenn ich die Anwendung weiterhin offline neu starten, wird die Anwendung Abgelaufene Informationen angezeigt. Nach der Kommunikation mit dem Back-End-Server wieder aufgenommen wird, würde der Nachrichtenfluss an den Back-End und der Endzustand würde aufgelöst, was der Benutzer erwartet wird. Doch bis zu diesem Zeitpunkt anzeigen die Anwendung die Informationen, die der Benutzer bereits lokal geändert hat.

Für Anwendungen, die erwarten, längere trennen dass, Don ’t abhängig Nachricht Cache; stattdessen implementieren ein Modell, das nach jeder Benutzeroperation beibehalten hat.

Für die Anwendung Alexandria erweitert ich die Zwischenspeicherung Konventionen sofort ablaufen, alle Informationen, die Teil eines Batch-Befehl und Abfragen angezeigt, z. B. in Abbildung 2 ist. Auf diese Weise wird nicht die aktuelle Informationen, und es werden ich auch nicht angezeigt fehlerhafte Informationen, wenn die Anwendung neu gestartet wird, bevor ich auf dem Back-End-Server eine Antwort erhalten. Für die Zwecke der Anwendung Alexandria ist ausreichend.

Back-End-Verarbeitung

Nun, da Sie Verständnis der Funktionsweise des Prozesses auf der Front-End-Seite der Dinge let’s sehen Sie sich den Code aus der Sicht des Back-End-Server. Sie kennen bereits die Verarbeitung von Abfragen, die ich im vorherigen Artikel gezeigt. Abbildung 3 zeigt den Code für die Verarbeitung eines Befehls.

Abbildung 3 Hinzufügen eines Buchs in der User-Warteschlange

public class AddBookToQueueConsumer : 
  ConsumerOf<AddBookToQueue> {

  private readonly ISession session;

  public AddBookToQueueConsumer(ISession session) {
    this.session = session;
  }

  public void Consume(AddBookToQueue message) {
    var user = session.Get<User>(message.UserId);
    var book = session.Get<Book>(message.BookId);

    Console.WriteLine("Adding {0} to {1}'s queue",
      book.Name, user.Name);

    user.AddToQueue(book);
  }
}

Der eigentliche Code ist ziemlich langweilig. Laden die relevanten Entitäten, und rufen Sie eine Methode für die Entität, die die aktuelle Aufgabe auszuführen. Dies ist jedoch noch wichtiger ist, als Sie vielleicht denken. Ein Architekt Auftrag, ich würde argumentieren, um sicherzustellen, dass die Entwickler im Projekt als bored wie möglich ist. Die meisten Unternehmen Probleme sind langweilig und vom technische Komplexitäten aus dem System entfernen, erhalten Sie einen viel höheren Prozentsatz Entwickler aufgewendete Arbeitszeit auf langweiligen Geschäftsprobleme anstelle von interessanten technologische Probleme.

Was bedeutet im Kontext des Alexandria? Anstelle von Geschäftslogik in die Nachricht-Nutzer zuweisen haben ich so weit wie möglich in den Entitäten die Geschäftslogik zentralisiert. Idealerweise folgt das Verarbeiten einer Nachricht dieses Muster:

  • Laden Sie alle Daten, die zur Verarbeitung der Nachricht erforderlich
  • Rufen Sie eine Methode für eine Domänenentität zum Ausführen des aktuellen Vorgangs

Dadurch wird sichergestellt, dass die Domänenlogik der in der Domäne bleiben geht. Für welche diese Logik wird – gut, die den Szenarien ist, richten Sie behandeln müssen. Dies sollte Ihnen einen Eindruck über die Behandlung ich die Domänenlogik im Fall von User.AddToQueue(book):

public virtual void AddToQueue(Book book) {
  if (Queue.Contains(book) == false)
    Queue.Add(book);
  Recommendations.Remove(book);

  // Any other business logic related to 
  // adding a book to the queue
}

Sie haben gesehen, einen Fall, in dem die Front-End-Logik und die Back-End-Logik genau übereinstimmen. Jetzt let’s betrachten einen Fall, in dem Sie Don ’ t. Ein Buch aus der Warteschlange zu entfernen, ist sehr einfach auf der Vorderseite ( Abbildung 4 erkennen). Es ist ziemlich einfach. Entfernen Sie das Buch aus der Warteschlange lokal (die von der Benutzeroberfläche entfernt), senden Sie einen Nachricht Batch an den Back-End, auffordert, um das Buch aus der Warteschlange entfernen und in der Warteschlange und die Empfehlungen zu aktualisieren.

Abbildung 4 ein Adressbuch aus der Warteschlange entfernen

public void RemoveFromQueue(BookModel book) {
  Queue.Remove(book);

  bus.Send(
    new RemoveBookFromQueue {
      UserId = userId,
      BookId = book.Id
    },
    new MyQueueQuery {
      UserId = userId
    },
    new MyRecommendationsQuery {
      UserId = userId
    });
}

Auf dem Back-End folgt Verarbeiten der Nachricht RemoveBookFromQueue das Muster in Abbildung 3 , laden die Entitäten und Aufrufen der Methode user.RemoveFromQueue(book) dargestellt:

public virtual void RemoveFromQueue(Book book) {
  Queue.Remove(book);
  // If it was on the queue, it probably means that the user
  // might want to read it again, so let us recommend it
  Recommendations.Add(book);
  // Business logic related to removing book from queue
}

Das Verhalten unterscheidet sich zwischen dem front-End- und Back-End. Auf dem Back-End fügen Sie entfernte Buch mit den Empfehlungen, die ich nicht auf die front-End. Was wäre das Ergebnis der geschätzten?

Gut, die unmittelbare Antwort wäre, das Buch aus der Warteschlange zu entfernen, aber als front-End die Antworten von den Back-End-Server erreicht ist, sehen Sie das Buch Empfehlungen Liste hinzugefügt. In der Praxis würden Sie vermutlich einen Unterschied feststellen, nur dann, wenn der Back-End-Server beendet wurde, wenn Sie ein Buch aus der Warteschlange entfernen sollen.

Das ist alles sehr schön, aber was, wenn Sie tatsächlich eine Bestätigung vom Back-End-Server zum Beenden eines Vorgangs benötigen?

Komplexe Operationen

Der Benutzer möchte hinzufügen, entfernen oder neu anordnen der Elemente in Ihrer Warteschlange ab, ist es ziemlich offensichtlich, dass die Operation nie fehlschlagen kann, so dass Sie die Anwendung die Operation sofort akzeptieren zu können. Für Operationen wie Adressen bearbeiten oder ändern die Kreditkarte aus, Sie können nicht einfach akzeptiert jedoch den Vorgang, bis Sie eine Bestätigung des erfolgreichen vom Back-End.

In Alexandria wird dies als ein vier Schritten implementiert. Kostüm klingt, aber es ist wirklich ganz einfach. Abbildung 5 zeigt die möglichen Stufen.

vier mögliche Stufen für eine Bestätigung anfordern Command

Abbildung 5 vier mögliche Stufen für eine Bestätigung anfordern Command

In der oberen linken Bildschirmabbildung zeigt die Normalansicht die Abonnement-Details. Dies ist wie Alexandria Änderungen bestätigte wird. In der unteren linken Bildschirmabbildung zeigt den Bildschirm bearbeiten für die gleichen Daten. Klicken Sie auf den Speichervorgang auf diesem Bildschirm führt im Screenshot auf die Top–right angezeigt; dies ist wie Alexandria nicht bestätigte Änderungen veranschaulicht.

Das heißt, ich stimme zu die Änderung (vorläufig) bis ich eine Antwort vom Server angibt erhalte, dass die Änderung akzeptiert wurde (was uns wieder auf dem Bildschirm oben links verschoben) oder abgelehnt, die den Prozess auf dem Screenshot unten rechts verschiebt. Dieses Bildschirmabbild zeigt einen Fehler vom Server und ermöglicht dem Benutzer das fehlerhafte Detail zu beheben.

Die Implementierung ist komplex, trotz der Meinung möglicherweise nicht. Ich werde im Back-End starten und nach außen zu verschieben. Abbildung 6 zeigt den Back-End-Code erforderlich, um dies zu behandeln, und es nicht alles neu. Ich habe viel dieselben Schritte in diesem Artikel durchführen, wurden. Befindet sich der Großteil der Funktionalität der bedingten Befehl (und Komplexität) im front-End.

Abbildung 6 Back-End-Verarbeitung der Änderung eines Benutzers Adresse

public void Consume(UpdateAddress message) {
  int result;
  // pretend we call some address validation service
  if (int.TryParse(message.Details.HouseNumber, out result) == 
    false || result % 2 == 0) {
    bus.Reply(new UpdateDetailsResult {
      Success = false,
      ErrorMessage = "House number must be odd number",
      UserId = message.UserId
    });
  }
  else {
    var user = session.Get<User>(message.UserId);
    user.ChangeAddress(
      message.Details.Street,
      message.Details.HouseNumber,
      message.Details.City, 
      message.Details.Country, 
      message.Details.ZipCode);

    bus.Reply(new UpdateDetailsResult {
      Success = true,
      UserId = message.UserId
    });
  }
}

Was unterscheidet was Sie vor dem gesehen haben wird, die hier ich explizite Erfolg bzw. Fehlschlag Code für diesen Vorgang haben, während bevor ich einfach eine Datenaktualisierung in eine separate Abfrage angefordert. Fehlschlagen der Operation können , und ich würde gern wissen, nicht nur, ob der Vorgang erfolgreich ist oder nicht, sondern Warum fehlgeschlagen ist.

Alexandria nutzt die Caliburn Framework zum Großteil der Drudgery Verwalten von die Benutzeroberfläche zu behandeln. Caliburn (caliburn.codeplex.com ) ist ein WPF/Silverlight-Framework, das stark abhängig von Konventionen erleichtern viele der Anwendungsfunktionen Code hinter den XAML-Code zu schreiben, anstatt das Anwendungsmodell erstellen.

Wie Sie sehen werden, aus der Beispielcode betrachtet ist fast alles auf der Benutzeroberfläche Alexandria verdrahtet über das XAML-Konventionen verwenden, was Ihnen klar und verständlich, XAML und einem Anwendungsmodell, die die Benutzeroberfläche direkt, wiedergibt ohne dass eine direkte Beziehung darauf. Dies führt dazu, dass Code wesentlich einfacher.

Abbildung 7 sollten Sie eine Vorstellung zu, wie dies im Modell SubscriptionDetails Ansicht implementiert wird. Im Wesentlichen SubscriptionDetails enthält zwei Kopien der Daten; eine wird in bearbeitbaren Eigenschaft gehalten und, was alle Ansichten im Hinblick auf Bearbeiten oder Anzeigen von Änderungen nicht bestätigte angezeigt wird. Die zweite wird in die Details-Eigenschaft gespeichert, die verwendet wird, um die bestätigten Änderungen aufzunehmen. Jeder Modus hat eine andere Ansicht, und jeder Modus auswählt, aus der die Daten angezeigt.

Abbildung 7 Verschieben zwischen den Modi der Ansicht in Reaktion auf Benutzereingaben

public void BeginEdit() {
  ViewMode = ViewMode.Editing;

  Editable.Name = Details.Name;
  Editable.Street = Details.Street;
  Editable.HouseNumber = Details.HouseNumber;
  Editable.City = Details.City;
  Editable.ZipCode = Details.ZipCode;
  Editable.Country = Details.Country;
  // This field is explicitly ommitted
  // Editable.CreditCard = Details.CreditCard;
  ErrorMessage = null;
}

public void CancelEdit() {
  ViewMode = ViewMode.Confirmed;
  Editable = new ContactInfo();
  ErrorMessage = null;
}

In XAML verdrahtet ich die ViewMode-Bindung, wählen Sie die gewünschte Ansicht für jeden Modus angezeigt werden. Mit anderen Worten, führt Wechseln zum Bearbeiten des Modus Views.SubscriptionDetails.Editing.xaml Ansicht wird den Bildschirm bearbeiten, für die das Objekt zum Anzeigen ausgewählt.

Es ist das Speichern und Bestätigung Prozesse werden am interessantesten, jedoch. Hier ist wie ich behandeln speichern:

public void Save() {
  ViewMode = ViewMode.ChangesPending;
  // Add logic to handle credit card changes
  bus.Send(new UpdateAddress {
    UserId = userId,
    Details = new AddressDTO {
      Street = Editable.Street,
      HouseNumber = Editable.HouseNumber,
      City = Editable.City,
      ZipCode = Editable.ZipCode,
      Country = Editable.Country,
    }
  });
}

Das einzige, was, das ich hier tatsächlich tun habe, eine Nachricht zu senden und das Wechseln der Ansicht zu einem nicht bearbeitbaren mit einen Marker, die besagt, dass die Änderungen noch nicht akzeptiert wurden. Abbildung 8 zeigt den Code für die Bestätigung oder Ablehnung. Alles in allem ordnet eine miniscule Menge Code zum Implementieren einer solchen Funktion und die Grundlage für die Implementierung von ähnlichen Features in der Zukunft.

Abbildung 8 Antworten verwenden und das Ergebnis der Verarbeitung

public class UpdateAddressResultConsumer : 
  ConsumerOf<UpdateAddressResult> {
  private readonly ApplicationModel applicationModel;

  public UpdateAddressResultConsumer(
    ApplicationModel applicationModel) {

    this.applicationModel = applicationModel;
  }

  public void Consume(UpdateAddressResult message) {
    if(message.Success) {
      applicationModel.SubscriptionDetails.CompleteEdit();
    }
    else {
      applicationModel.SubscriptionDetails.ErrorEdit(
        message.ErrorMessage);
    }
  }
}

//from SubscriptionDetails
public void CompleteEdit() {
  Details = Editable;
  Editable = new ContactInfo();
  ErrorMessage = null;
  ViewMode = ViewMode.Confirmed;
}

public void ErrorEdit(string theErrorMessage) {
  ViewMode = ViewMode.Error;
  ErrorMessage = theErrorMessage;
}

Außerdem müssen Sie die herkömmliche Anforderungs-/Antwort-Aufrufe, wie das Durchsuchen des Katalogs berücksichtigt. Da Kommunikation in solche Aufrufe über unidirektionale Meldungen erreicht ist, müssen Sie zum Ändern der Benutzeroberfläche an die Hintergrundverarbeitung, bis die Antwort vom Back-End-Server eingeht. Ich wird nicht über diesen Prozess im Detail gehen, aber der entsprechende Code darauf in der Beispielanwendung vorhanden ist.

Auschecken

Am Ende dieses Projekts gestartet ich besagt, die Ziele und die Herausforderungen, die ich beim Erstellen einer solchen Anwendung verbundenen erwartet. Wichtigsten Herausforderungen, die ich an Adresse vorgesehen waren Datensynchronisierung, die Fallacies der verteilten Datenverarbeitung und Behandeln eines zeitweise verbundenen Clients. Erneut suchen, meiner Meinung nach Alexandria gut meiner Ziele erfüllen und überwinden der Herausforderungen unterstützt.

Die Front-End-Anwendung basiert auf WPF und Erstellen starker der Caliburn Konventionen verringern den eigentlichen Code für das Anwendungsmodell verwenden. Das Modell ist für die XAML-Ansichten und eine kleine Gruppe von Front-End-Nachricht Verbraucher, die Aufrufe an das Objektmodell der Anwendung gebunden.

Ich behandelt die Behandlung von unidirektionalen messaging, Zwischenspeichern von Nachrichten auf der Ebene der Infrastruktur und zulassen für nicht verbundene Arbeit auch für Vorgänge, die Back-End-Genehmigung erfordern, bevor Sie tatsächlich abgeschlossen angesehen werden können.

Back-End erstellt habe ich eine Nachricht basierende Anwendung, die anhand von Rhino Service Bus und NHibernate. Erörtert Verwaltung der Gültigkeitsdauer von Sitzungs- und Transaktion und wie Sie Nachrichten Batches mit NHibernate äußersten Cache nutzen können. Die Nachricht Nutzer auf dem Back-End dienen für einfache Abfragen oder als Delegators für die entsprechende Methode für ein Domänenobjekt, den größten Teil der Geschäftslogik sich tatsächlich befindet.

Erzwingen die Verwendung von expliziten Befehle, sondern eine einfache CRUD-Schnittstelle führt eine übersichtlichere Code. Da die gesamte Architektur schwerpunktmäßig klar definieren die Rolle von jeder Teil der Anwendung, und wie Sie erstellt werden, sollten haben, können Sie einfach, den Code zu ändern. Das Endergebnis ist eine sehr strukturierten Produkt mit klaren Linien Zuständigkeitsbereich.

Es ist schwer, um Anleitungen für eine vollständige verteilten Anwendungsarchitektur in ein paar kurze Artikel vor allem während des Versuchs, die gleichzeitig mehrere neue Konzepte vor drücken. Weiterhin, denke ich, dass Sie feststellen werden, dass die hier beschriebenen Vorgehensweisen anwenden Anwendungen, die tatsächlich führt arbeiten mit einfacher als die herkömmlichen RPC oder CRUD-basierten Architekturen sind.

Oren Eini arbeitet unter dem Pseudonym Ayende Rahien. Er ist aktives Mitglied verschiedener Open Source-Projekte, darunter NHibernate und Castle, und Gründer vieler anderer, darunter Rhino Mocks, NHibernate Query Analyzer und Rhino Commons. Eini ist auch verantwortlich für die NHibernate Profiler ( nhprof.com ), einen visual Debugger für NHibernate. Führen Sie seine Arbeit ayende.com/Blog-.