Work in Progress
Diese Seite ist aktuell im Review! Die Seite wurde noch nicht qualitätsgesichert und kann Fehler enthalten.
Die verlinkten Seiten sind ggf. nur für Schleupen-Mitarbeiter sichtbar.

Transactional Outbox

Mit dem Begriff Transactional Outbox wird ein allgemeines Implementierungsmuster bezeichnet, bei dem das Versenden von Nachrichten durch eine Transaktion abgesichert wird direkt zu versenden, werden sie innerhalb der Transaktion in der Datenbank gespeichert und erst nachgelagert bei einem erfolgreichen Commit an das Messaging-System weitergegeben. Dadurch ist sichergestellt, dass die Nachrichten konsistent zu den Datenänderungen des Domänenmodells innerhalb der Transaktion sind. Darüber hinaus wird bei einem Rollback der Transaktion verhindert, dass die Nachrichten überhaupt zugestellt werden.

Die Transactional Outbox ist dabei nur ein Mechanismus, um das Versenden von Messages resilient zu implementieren (vgl. Resiliente Services / Resiliente "Service" anbindung). Hierzu gehört auch das Retry-Pattern, ein Circuit-Breaker uvm.!

Seit der HV22 können für alle Services Messages als Commands über die Transactional Outbox versendet werden. Damit unterscheidet sich die Service-Kategorie Command Service von den anderen Service-Kategorien insbesondere dadurch, dass Command Services nur über das Messaging angesprochen werden können. Aufrufe über den Broker führen bei einem Command Service immer dazu, dass die Nachricht an das Messaging-System übergeben statt direkt zugestellt wird.

Mit der FV21 wird die Transactional Outbox sowohl für Business Events als auch für Requests an Command Services unterstützt. Das Muster liefert insgesamt die folgenden Garantien:

  • Innerhalb einer Transaktion über die Outbox ausgelöste (valide) Events/Command Messages werden garantiert irgendwann an das Messaging-System übergeben. Schlägt das Versenden direkt nach dem Commit der Transaktion fehl, finden in regelmäßigen Abständen weitere Versuche statt, bis die Übergabe erfolgreich war.
  • Das Messaging-System ist nach der Annahme einer Nachricht für ihre endgültige Zustellung an den Ziel-Service nach dem At-Least-Once-Prinzip verantwortlich.

    In der aktuellen 3.0-Architektur übernimmt der Business Event Dispatcher (BED) die Aufgabe, Nachrichten an den Ziel-Service zu vermitteln. Zustellversuche werden hier zwar wiederholt, nach mehrmaligen Retries werden nicht erfolgreich zugestellte Nachrichten jedoch als Dead Letter aussortiert und landen im Message Store.

Fachtransaktionen in Schleupen.CS werden durch Datenbanktranskationen über das .NET Mittel der Ambient Transactions (-> TransactionScope) abgebildet. Die Information über die Änderung an Daten wird im Allgemeinen durch den Event-Mechanismus mitgeteilt. Das kombinierte Versenden von Events und der Abschluss von Datenbanktransaktionen ist in einer SOA jedoch essentiell.

Events sollen dann, und nur dann ausgelöst werden, nachdem eine zugehörige Transaktion abgeschlossen werden konnte!

Die Transactional Outbox bietet die Mechanismen und Garantien, um diesen Grundsatz durchzusetzen.

Grober Ablauf
  1. Service wird aufgerufen
  2. Innerhalb einer lokalen Transaktion werden die fachlichen Daten des Event (Message) in die Tabelle OutboxMessage gespeichert
  3. Job liest aus der Tabelle OutboxMessage und stellt die Nachricht in die Ziel-Queue ein
Optimiert
  1. Service wird aufgerufen
  2. Innerhalb einer lokalen Transaktion werden die fachlichen Daten des Event (Message) in die Tabelle OutboxMessage gespeichert
  3. Job liest aus der Tabelle OutboxMessage und stellt die Nachricht in die Ziel-Queue ein
    • (a) nach Transaktionsabschluss wird die Nachricht in die Ziel-Queue eingestellt

Design

Nachrichten (Business Events/Command Messages) werden innerhalb der Fachtransaktion im Schema der Fachdatenbank gespeichert. Dadurch ist sichergestellt, dass die Nachrichten immer transaktional synchron zu den Fachdaten sichtbar sind. Im Anschluss an den erfolgreichen Abschluss der Transaktion werden die Nachrichten über einen Hintergrund-Thread (OutboxDeliveryWorker) an das Messaging-System übergeben. Im Erfolgsfall werden die Nachrichten aus der Datenbanktabelle entfernt. Tritt ein Fehler in der Fachtransaktion auf, werden sowohl die fachlichen Daten also auch die Nachrichten zurückgerollt.

Sollte die Übergabe an das Messaging-System scheitern, so werden die zwischengespeicherten Nachrichten asynchron durch einen Scheduled Service im Nachgang versendet, um die At-Least-One Garantie aufrecht zu erhalten. Der Scheduled Service heißt aus Kompatibilitätsgründen RaiseDeferredBusinessEventsAggregatingActivityService. Er ruft sowohl die durch die Transactional Outbox API im Land bereitgestellten Services (ProcessTransactionalOutboxActivityService) als auch die durch die Deferred Events API bereitgestellten Services (RaiseDeferredBusinessEventsActivityService) auf. Auf diese Weise werden beide APIs parallel unterstützt.

Dependency Injection spielt im Design der Transactional Outbox eine wesentliche Rolle. Je nach Kontext werden unterschiedliche Implementierungen der internen Interfaces mit unterschiedlichen Lifestyles genutzt. Im Service-Kontext, d.h. in der Landimplementierung, sind die verwendeten Instanzen vorwiegend auf den Scope der WCF-Operation beschränkt und nutzen insbesondere innerhalb der Fachtransaktionen dieselben Datenbankverbindungen wie die Repositories des Landes, in dem die API angeschlossen ist. Dadurch werden verteilte Transaktionen vermieden. Der Hintergrund-Thread zum nachgelagerten Versenden der zwischengespeicherten Nachrichten nutzt hingegen die Informationen der SessionTokens aus den Service-Operationen, um unabhängig Datenbankverbindungen zu erzeugen. Die injizierten Instanzen haben vorwiegend den Lifestyle Transient, da der Hintergrund-Thread nicht innerhalb des WCF-Kontexts läuft.

Die Deferred Events API selbst baut seit der FV21 auf der Transactional Outbox API auf und nutzt deren Implementierung. Der Zugriff auf die Datenbanktabelle DeferredBusinessEvents wird dabei transparent per Dependency Injection geregelt. Nach außen sind jedoch alle Schnittstellen und das Verhalten unverändert geblieben.

Implementierung

Die Pakete

  • Schleupen.CS.PI.SB.TransactionalOutbox
  • Schleupen.CS.PI.SB.TransactionalOutbox.Castle3

enthalten die Implementierung der Transactional Outbox sowie den Anschluss per Dependency Injection. Um diese nutzen zu können, müssen sie auf dem üblichen Weg per Paket referenziert werden.

Über das Vorhandensein der Assembly Schleupen.CS.PI.SB.TransactionalOutbox.dll im Projekt generiert Sigento dann Code (-> ServiceClientAdapter), der verwendet werden kann. Dieses Muster verwendet diesen Code aber nicht mehr (ist im Vorläufer-Muster aber noch enthalten), um den Code in Summe nachvollziehbarer (und somit einfacher) zu haben. Aus Gründen der Kompatibilität wird der Code auch weiterhin generiert.

Auslösen von Events

Um innerhalb einer Transaktion Events zwischenzuspeichern, die nach dem erfolgreichen Commit automatisch ausgelöst werden, muss (im Standard) die Schnittstelle IBusinessEventPublisher verwendet werden. Der folgende Code zeigt ein Beispiel dazu (vgl. Gateway zu entfernten Objekten - Events)):

ITransactionScopeFactory transactionScopeFactory = ... // DI
using (ITransactionScope transactionScope = transactionScopeFactory.CreateTransactionScope())
{
    IBuchRepository repository = ...;

    // Domänenlogik

    buchCreatedEventServiceGateway.Raise(neueBuecher);
    repository.Flush();

    transactionScope.Complete();
}

Für das Muster der Transaktional Outbox ist es unerheblich, ob das Gateway im Controller oder per PublishAggregateChangesExtender oder per DomainEventHandler angeschlossen ist!

public sealed class BuchCreatedEventServiceGateway : IBuchCreatedEventServiceGateway
{
    private readonly IBusinessEventPublisher businessEventPublisher;

    // ...

    public void Raise(IEnumerable<Guid> neueBuecher)
    {
        // ...
        RaisedNotification notification = new
        RaisedNotification(sessionToken, buchIds);

        businessEventPublisher
            .PublishOnTransactionComplete<IBuchCreatedEventService, RaisedNotification>(
                notification); // -> transaktional, speichert innerhalb der Transaktion in der Tabelle 'OutboxMessage' 
                               // löst das Event bei Transaktionsabschluss direkt aus und löscht dieses aus der Tabelle
    }
}

Ab Schleupen.CS.PI.SB.API, Version 3.29.1.17 muss das SessionToken nicht mehr gesetzt werden. Diese wird dann nur gesetzt, wenn kein SessionToken gesetzt wurde, die Eigenschaft oder das Feld schreibend und lesend verwendet werden kann!

Optional können die zwischengespeicherten Events auch manuell statt automatisch nach dem Commit der Transaktion ausgelöst werden. Dazu muss statt der Schnittstelle IBusinessEventPublisher stattdessen IBusinessEventManager verwendet werden. Damit die Events beim Abschluss der Transaktion nicht sofort ausgelöst werden, muss anstatt PublishOnTransactionComplete<>() die Methode Store<>() mit den identischen Argumenten aufgerufen werden. Das explizite Auslösen findet dann nach der Transaktion über die Methode Publish() statt. Das folgende Beispiel zeigt nur das Auslösen.

IBusinessEventManager businessEventManager = ...;

ITransactionScopeFactory transactionScopeFactory = ... // DI
 using (ITransactionScope transactionScope = transactionScopeFactory.CreateTransactionScope())
{
    // Domänenlogik ...

    RaisedNotification notification = new RaisedNotification(sessionToken, buchIds);
    businessEventManager.Store<IBuchCreatedEventService, RaisedNotification>(notification);

    transactionScope.Complete();
}

businessEventManager.Publish();
Job
Aggregation von Events

Bei der direkten Auslösung von Events aus Repositories kann es zu Konstellationen kommen, in denen viele Einzelevents ausgelöst werden, anstatt ein Sammelevent zu generieren. Ein Beispiel hierfür ist der Aufruf des BuchEntityService mit einer Liste von Büchern. Hier wird, obwohl der Service einmal mit einer Liste von Büchern aufgerufen wird für jedes Buch ein eigenes BuchCreated-Event geworfen, was vor allem in der Massenverarbeitung problematisch sein kann.

Es ist hier möglich, für Events so genannte Aggregatoren bereitzustellen, die die Einzelevents zu einem Sammelevent zusammenfassen. Ein Aggregator hält Informationen über eine Gruppe von Events, die zusammegefasst werden sollen. Hier werden in der Regel Ids aus mehren Events zusammengefasst, um dann nachher ein Event zu erzeugen, das all diese Ids enthält. Aggregatoren erben von der abstrakten Basisklasse GroupBySessionTokenBusinessEventAggregator, die das Interface IBusinessEventAggregator implementiert und müssen auf dem DI Container registriert sein. Ein Aggregator darf keine Abhänigkeiten auf WCF-Scoped Objekten z.B. IConfiguration, weil die Aggregatoren ausserhalb des WCF.OperationContext (also im Background-Thread) verwendet werden. Daher werden deswegen als Transisient registiert.

internal class BuchCreatedBusinessEventAggregator
    : GroupBySessionTokenBusinessEventAggregator<IBuchCreatedEventService, RaisedNotification>
{
    protected override string ReadSessionTokenFromEvent(RaisedNotification @event)
    {
        // ...
    }

    protected override RaisedNotification[] CreateAggregatedEvents(
        string sessionToken,
        IEnumerable<RaisedNotification> events)
    {
        // ...
    }
}

Im Beispiel dieses EventAggregators muss keine explizite Registrierung in Windsor Container vorgenommen werden, da die Klasse im Gateways Projekt definiert ist (wird im ServiceHostConfigurator bei den Assemblies indiziert) und der Namenskonvention für die automatische Registrierung entspricht!

Wähle stets einen Namen der Form <EventName>BusinessEventAggregator damit eine automatische Registrierung im DI-Container erfolgt. Dann wird diese automatisch angebunden.

Die generischen Parameter geben das Interface an, auf dem das Event ausgelöst wird (in diesem Fall IBuchCreatedEventService) und den Typen des Events (in diesem Fall RaisedNotification). Zusätzlich muss eine Methode implementiert werden:

  • CreateAggregatedEvents liefert die aggregierten Events zurück. Die aggregierten Events haben den gleichen Typen, wie die Originalevents. Es ist möglich, falls die Aggregation z.B. tausende von Events zusammenfasst auch hier mehrere Events zurückzugeben, die die Daten partitioniert enthalten.

Falls die SessionToken nicht als Feld oder Eigenschaft SessionToken, muss die Methode ReadSessionTokenFromEvent überschrieben werden.

Aggregatoren Dürfen kein Abhänigkeiten zu WCF-Scoped Objekten haben (z.B. IConfiguration) => Grund: Aggregatoren werden ausserhalb WCF.OperationContext (in Background-Thread) und werden deswegen als Transient registriert

Um Subscriber vor Überlastung zu schützen muss in CreateAggregatedEvents die Menge sinnvoll unterteilt werden (z.B. mit .Partition(.)).
Außerdem muss davon ausgegangen werden, dass die Aggregation sowohl direkt bei der Request-Verarbeitung im Service genutzt wird, als auch durch den Auslöse-Job (falls die direkte Auslösung der Events gescheitert ist).

Es dürfen nicht mehrere Aggregatoren registriert sein, die den gleichen Eventtypen bearbeiten. In diesem Fall kommt es zu einem Laufzeitfehler.

Hier eine konkrete Standard-Implementierung:

internal class BuchDeletedBusinessEventAggregator
    : GroupBySessionTokenBusinessEventAggregator<IBuchDeletedEventService, RaisedNotification>
{
    // Muss implementiert werden. Aggregiert Events. 
    // Um Subscriber vor Überlastung zu schützen, muss die Menge sinnvoll unterteilt werden (zB mit .Partition(…)).
    protected override RaisedNotification[] CreateAggregatedEvents(string sessionToken, IEnumerable<RaisedNotification> events)
    {
        if (events == null) { throw new ArgumentNullException(nameof(events)); }

        List<RaisedNotification> notifications = new List<RaisedNotification>();
        // Hinweis: Die Partitionierung ist ggf.notwendig, damit die Message nicht zu groß wird!
        foreach (var partition in events.SelectMany(e => e.BuchIds).Partition(PartitionSize))
        {
            notifications.Add(new RaisedNotification(sessionToken, new ArrayOfId(partition)));
        }

        return notifications.ToArray();
    }
}

Die Aggregation von Events findet vor der direkten Auslösung sowie im nachgelagerten Job statt, d.h. die zusammengefassten Events werden aus der DeferredBusinessEvent-Tabelle gelöscht und durch die Sammelevents ersetzt.

Versenden von Command Messages

Nachrichten an Command Services können über die Outbox nur explizit versendet werden, d.h., die von Sigento generierten Service Clients kapseln nicht wie bei den Business Events die Logik zum Anschluss der Transactional Outbox. Anstatt den Service-Aufruf direkt per Service Client durchzuführen, muss die erstellte Nachricht stattdessen innerhalb einer Transaktion über das Interface ICommandMessageSender an die Outbox übergeben werden. Beim erfolgreichen Abschluss des Transaktion werden die Nachrichten dann direkt über einen Hintergrund-Thread in die entsprechende Queue im Messaging-System eingestellt. Das folgende Beispiel zeigt den Anschluss der Outbox über ein Gateway (vgl. Gateway zu entfernten Objekten - Commands).

ITransactionScopeFactory transactionScopeFactory = ... // DI
 using (ITransactionScope transactionScope = transactionScopeFactory.CreateTransactionScope())
{
    ISomethingRepository repository = ...;

    // Domänenlogik

    doSomethingActivityServiceGateway.Execute(newSomethings); // DoSomethingActivityService muss eine Command Service sein
    repository.Flush();

    transactionScope.Complete();
}
public sealed class DoSomethingActivityServiceGateway : IDoSomethingActivityServiceGateway
{
    private readonly ICommandMessageSender commandMessageSender;

    // ...

    public void Execute(IEnumerable<Guid> newSomethings)
    {
        ExecuteRequest request = new ExecuteRequest(sessionToken, newSomethings);
        commandMessageSender.DeliverOnTransactionComplete<IDoSomethingActivityService, ExecuteRequest>(request); // -> transaktional, speichert innerhalb der Transaktion in der Tabelle 'OutboxMessage' // sendet die Message bei Transaktionsabschluss direkt an die Queue und löscht dieses aus der Tabelle
    }
}

Aggregationen werden für Command Messages nicht unterstützt. Es obliegt daher dem Erzeuger der Nachricht, diese entsprechend zu portionieren.

Service Interfaces mit mehreren Implementierungen

Bei Service Interfaces mit mehreren Service-Implementierungen (s. Attribut HasMultipleImplementations) ist das Ziel einer Command Message allein anhand der Schnittstelle nicht eindeutig. Da Commands als Message Exchange Pattern jedoch eine gerichtete 1:1-Kommunikation beschreiben, muss in diesem Fall die konkrete Service-Implementierung als Ziel explizit angegeben werden. Dazu enthält die Schnittstelle ICommandMessageSender einen entsprechenden Overload der Methode DeliverOnTransactionComplete(). Über den zusätzlichen Parameter vom Typ CommandPublishingConfig kann der Name der Komponente, die die Service-Implementierung enthält, angegeben werden. Den Konponentennamen nicht anzugeben, ist ein Fehler und führt zu einer Exception. Bei Service Interfaces mit eindeutiger Service-Implementierung wird ein angegebener Komponentenname hingegen ignoriert.

Die Komponente einer Service-Implementierung entspricht der Webanwendung, die den Service hostet. Dementsprechend kann der Name einer Komponente aus der ServiceImplementationId hergeleitet werden. Für das Beispiel Schleupen.AS.MT.BIB.Services/Schleupen.AS.MT.BIB.Buecher.EntityService_3.2.BuchEntityService.svc entspricht der Komponentenname dem unterstrichenen Anteil der ServiceImplementationId.

Das folgende Beispiel zeigt die Nutzung der Transactional Outbox zum Versenden einer Command Message unter Angabe der Zielkomponente.

public sealed class DoSomethingActivityServiceGateway : IDoSomethingActivityServiceGateway
{
    private readonly ICommandMessageSender commandMessageSender;

    // ...

    public void Execute(IEnumerable<Guid> newSomethings)
    {
        ExecuteRequest request = ExecuteRequest(sessionToken, newSomethings);
        commandMessageSender.DeliverOnTransactionComplete<IDoSomethingActivityService, ExecuteRequest>(
          request,
          new CommandPublishingConfig { ComponentName = "Schleupen.CS.MT.BIB.Services" });
    }
}

Über die String-Konstante CommandPublishingConfig.AnyComponent als ComponentName kann ein Broadcast an alle Implementierungen eines Service Interfaces mit mehreren Implementierungen ausgelöst werden. Diese Möglichkeit sollte nur mit Bedacht eingesetzt werden.

Dependency Injection

Die Implementierungen der Schnittstellen ICommandMessageSender und IBusinessEventPublisher (bzw. IBusinessEventOutbox und IBusinessEventManager) werden per Dependency Injection zur Verfügung gestellt. Um die Funktionalität der Outbox nutzen zu können, muss also der entsprechende Installer aus dem Package Schleupen.CS.PI.SB.TransactionalOutbox.Castle3 eingebunden werden. Dabei müssen die benötigten Features (Support für Command Messages/Business Events) per Opt-In aktiviert werden. Dadurch ist es möglich, sich selektiv zu entscheiden, ob nur ein Nachrichtentyp unterstützt werden soll oder alle. Dies ist entscheidend bei der gemeinsamen Nutzung mit Deferred Events.

public class ServiceHostFactoryConfigurator : WcfContainerConfigurator
{
    // ...

    protected override void OnContainerConfigured(IWindsorContainer windsorContainer)
    {
        if (windsorContainer == null) { throw new ArgumentNullException(nameof(windsorContainer)); }

        windsorContainer.Install(new ServiceBusClientInstaller(IsInsideWcfOperation) { DatabaseSchema = "pi_sb_test" });
        windsorContainer.Install(new TransactionalOutboxInstaller(IsInsideWcfOperation)
        {
            EnableBusinessEvents = true, // Aktiviert die Unterstützung für Business Events
            EnableCommandMessages = true  // Aktiviert die Unterstützung für Command Messages
        });
        // ...

        base.OnContainerConfigured(windsorContainer);
    }
}

Mit der Referenzierung und der Anwendung des Installers stellt das entsprechende Service-Paket automatisch den ProcessTransactionalOutboxActivityService bereit. Dieser wird durch die Infrastruktur in regelmäßigen Intervallen aufgerufen und ist dafür zuständig, einen erneuten Zustellversuch von Nachrichten in der Outbox, die unmittelbar nach dem erfolgreichen Abschluss einer Transaktion nicht an das Messaging-System übergeben werden konnten, durchzuführen.

Nur für Plattform-Komponenten:
Der Installer stellt außerdem die Property EnableInfrastructureDbFallback zur Verfügung. Wird das Flag gesetzt, bewirkt es, dass Nachrichten (Events/Command Messages) in einem Service-Kontext ohne Session Token in der Infrastruktur-Datenbank zwischengespeichert werden. Die Tabelle OutboxMessage muss dazu im entsprechenden Schema vorhanden sein.

Datenbank-Patch

Jedes Land, das die Transactional Outbox nutzen will, muss eine entsprechende Tabelle innerhalb seines Schemas in der Datenbank anlegen. Der Datenbank-Patch kann das folgende Template verwenden, wobei XXX durch das jeweilige Schema im Land zu ersetzen ist.

CREATE TABLE [XXX].[OutboxMessage] (
    [Id] [uniqueidentifier] NOT NULL,
    [ClusterId] [INT] IDENTITY(1, 1) NOT NULL
    [ServiceInterfaceId] [VARCHAR](255) NOT NULL,
    [MessageXml] [VARCHAR](MAX) NOT NULL,
    [MessageType] [VARCHAR](255) NOT NULL,
    [MessageHandlingMetadata] [VARCHAR](MAX) NULL,
    [ERSTELLT] [datetimeoffset](0) NOT NULL CONSTRAINT [p_OutboxMessage] PRIMARY KEY NONCLUSTERED ([Id] ASC),
    CONSTRAINT [u_OutboxMessage_ClusterId] UNIQUE CLUSTERED ([ClusterId] ASC)
    );

REVOKE ALL PRIVILEGES
    ON "XXX".OutboxMessage
    FROM PUBLIC AS "csdbadmin";

GRANT SELECT,
      INSERT,
      UPDATE,
      DELETE
    ON "XXX".OutboxMessage
    TO csdbworker AS "csdbadmin";

GRANT SELECT
    ON "XXX".OutboxMessage
    TO csdbreader AS "csdbadmin";

ALTER TABLE [XXX].[OutboxMessage] ADD CONSTRAINT [d_OutboxMessage_ERSTELLT] DEFAULT(sysdatetimeoffset())
FOR [ERSTELLT];

Abgrenzung

Das Muster garantiert die Übergabe von Nachrichten an das Messaging-System, nicht die sichere Zustellung vom Messaging-System an den Ziel-Service (dies leistet die Infrastruktur).

In Teilen bietet die Transactional Outbox dieselbe Funktionalität wie die Deferred Events 1.1: Transaktionales Auslösen von Events (WebserviceCommunication, Transaktionaler EventClient, Deferred Events), ist jedoch mit der Unterstützung von Requests an Command Services eine direkte Weiterentwicklung. Zudem wurde die Datenhaltung von verschiedenen Nachrichtentypen innerhalb der Datenbank harmonisiert und das Design so verallgemeinert, dass zukünftig auch weitere Messaging Patterns oder auch beliebige Aktionen an den erfolgreichen Abschluss einer Transaktion gekoppelt werden können.

Stärken und Schwächen

Stärken
  • Transaktionssicheres Auslösen von Business Events und Versenden von Nachrichten an Command Services
Schwächen
  • Erhöhte Komplexität
  • Erhöhter Implementierungsaufwand

Migration

Kompatibilität von Deferred Events und der Transactional Outbox

Deferred Events und die Transactional Outbox können nur bedingt gemeinsam in einem Land genutzt werden. Sofern nur die Unterstützung für Command Messages (EnableCommandMessages = true) in der Transactional Outbox aktiviert ist, können Deferred Events verwendet werden. Es ist jedoch unzulässig, Deferred Events gleichzeitig mit der Transactional Outbox für Business Events (EnableBusinessEvents = true) zu kombinieren. Eine solche Konfiguration führt zu einem Laufzeitfehler und wird technisch unterbunden. Entwickler müssen sich bzgl. Business Events für einen von beiden Mechanismen entscheiden.

Deferred Events nach Transactional Outbox

Nach außen bieten Deferred Events und die Transactional Outbox für Business Events dieselbe Funktionalität an, sind technisch jedoch unterschiedlich aufgebaut. Für die Migration von Deferred Events zur neuen Transactional Outbox sind die folgenden Schritte notwendig:

  1. Referenzieren der neuen API-Pakete Schleupen.CS.PI.SB.TransactionalOutbox(.Castle3) anstatt Schleupen.CS.PI.SB.EventPublishingClient(.Castle3)
  2. Verwenden von TransactionalOutboxInstaller anstatt EventPublishingClientInstaller
  3. Anpassen der referenzierten Namespaces (using-Anweisungen) und verwendeten Typen (z.B. IBusinessEventPublisher statt IDeferredBusinessEventPublisher) im eigenen Code
  4. Sigento mit aktueller Version erneut auf WSDL-Dateien von Event Services anwenden
  5. Migrieren bestehender Einträge in der Tabelle DeferredBusinessEvent nach OutboxMessage und anschließen Tabelle DeferredBusinessEvent entfernen

Für die Migration der Datenbanktabelle ist das folgende Mapping der Spalten anzuwenden:

  • Id -> Id
  • ServiceInterfaceId -> ServiceInterfaceId
  • RaisedNotification -> MessageXml
  • ERSTELLT -> ERSTELLT

Die neu eingeführten Felder sind für alle migrierten Datensätze wie folgt zu belegen:

  • MessageType = 'BusinessEvent'
  • MessageHandlingMetadata = NULL
INSERT INTO [XXX].[OutboxMessage] (
    Id,
    ServiceInterfaceId,
    MessageXml,
    MessageType,
    MessageHandlingMetadata,
    ERSTELLT
    )
SELECT Id,
       ServiceInterfaceId,
       RaisedNotification,
       'BusinessEvent',
       NULL,
       ERSTELLT
FROM [XXX].DeferredBusinessEvent;

DROP TABLE [XXX].DeferredBusinessEvent;

Benutze Muster

Cookie Consent mit Real Cookie Banner