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.

Anschluss des Domänenmodells an die Service-Fassade - Usecase-Controller

Dieses Dokument beschreibt das Muster ServiceFacade, UseCaseController. Nach diesem Muster wird definiert, wie Services orchestriert werden, Daten geladen werden etc. und an die Programmlogik eines Domänenmodells delegiert wird.

Design

Ein Usecase-Controller beinhaltet die Schritte, die in einem Anwendungsfall bzw. dem assoziierten Aktivitätsmodell definiert sind. Der Usecase-Controller vermittelt zwischen dem Domänenmodell, den zur Abarbeitung benötigten Services und der Persistenzlogik. Jeder Schritt gemäß des Aktivitätsdiagramms kann demnach als Methode implementiert werden.

Daten der eigenen Applikation werden über die Persistenzlogik (i.A. über das Repository) geladen. Daten, die nicht unter der Verantwortlichkeit der eigenen Applikation obliegen, werden über fremde Dienste geladen und mit Hilfe von Gateways (siehe Muster Gateway zu entfernten Objekten) angesprochen. Ein Gateway ist dazwischenzuschalten, da ein Usecase-Controller selber nicht mit Datenverträgen arbeitet. Der Usecase-Controller stellt demnach den Einstieg in das Domänenmodell dar, wobei die eigentliche Logik im Domänenmodell implementiert wird. Der Usecase-Controller implementiert die Logik, die Anwendungsfall-bezogen eindeutig ist.

Der Usecase-Controller wird als Klasse in der Services-Assembly implementiert.

  • UseCase-Controller rufen sich gegenseitig nur in seltenen Fällen auf: Dann und nur dann, wenn der Anwendungsfall ein Unteranwendungsfall ist. Ggf. müssen separate Klassen extrahiert werden, um Logik redundanzfrei zu implementieren.
  • Es dürfen keine UseCase-Controller von anderen Applikationen aufgerufen werden.

Überlege, ob die Verwendung von Domain Events als Erweiterung hier sinnvoll ist, so dass der Usecase-Controller nur ein Aggregat schreibend ändert!

Implementierung anhand eines Beispiels

Die Klasse eines Usecase-Controllers wird nach folgender Konvention erstellt: <Name des Anwendungsfalls>Controller. Die weitere Implementierung wird anhand des Anwendungsfalls Archivserver verwalten beschrieben.

Zunächst muss die Klasse erstellt werden. Der Name lautet hier: AdministrateArchiveServersController.

public class BuecherAusleihenController : IBuecherAusleihenController
{
    private readonly IBenutzerkontoRepository benutzerkontoRepository;
    private readonly IBuchRepository buchRepository;
    private readonly IBuecherAusgeliehenEventServiceGateway buecherAusgeliehenEventServiceGateway;

    public BuecherAusleihenController(IBenutzerkontoRepository benutzerkontoRepository, 
                                      IBuchRepository buchRepository, 
                                      IBuecherAusgeliehenEventServiceGateway buecherAusgeliehenEventServiceGateway)
    {
        if (benutzerkontoRepository == null) { throw new ArgumentNullException(nameof(benutzerkontoRepository)); }
        if (buchRepository == null) { throw new ArgumentNullException(nameof(buchRepository)); }
        if (buecherAusgeliehenEventServiceGateway == null) { throw new ArgumentNullException(nameof(buecherAusgeliehenEventServiceGateway)); }

        this.benutzerkontoRepository = benutzerkontoRepository;
        this.buchRepository = buchRepository;
        this.buecherAusgeliehenEventServiceGateway = buecherAusgeliehenEventServiceGateway;
    }

    public Ausleihe LeiheAus(string benutzerSid, Guid[] buecherIds)
    {
        if (string.IsNullOrEmpty(benutzerSid)) { throw new ArgumentNullException(nameof(benutzerSid)); }
        if (buecherIds == null) { throw new ArgumentNullException(nameof(buecherIds)); }

        IEnumerable<Buch> buecher = buchRepository.QueryById(buecherIds);
        Benutzerkonto benutzerkonto = ReadBenutzerkonto(benutzerSid);

        Ausleihe ausleihe = benutzerkonto.LeiheAus(buecher);

        benutzerkontoRepository.Flush();
        buecherAusgeliehenEventServiceGateway.Raise(ausleihe);

        return ausleihe;
    }

    public void VerlaengereAusleihfrist(string benutzerSid, Guid ausleiheId, TimeSpan fristverlaengerung)
    {
        // ...
    }
}

Eine Methode des Anwendungsfalls wird beispielsweise wie bei LeiheAus(...) zu sehen implementiert.

Dabei wird - wenn möglich - folgende Reihenfolge verwendet:

  1. Laden der Daten (via Repositories für eigene Daten, Fremdservice für applikationsfremde Daten)
  2. Anstoßen der Domänenlogik (ggf. andere Usecase-Controller der eigenen Applikation)
  3. Speichern der geänderten Daten (via Fremdservice, Usecase-Controller oder eigene Aggregate)
Benennung

Beispiele für gute Namen:

  • AdministrateArchiveServersController
  • CleanUpProtocolEntriesController
  • PersistChangedPersonController

Beispiele für schlechte Namen:

  • PersonController (Person ist kein Anwendungsfall)
  • QueryController (Query ist als Anwendungsfall zu allgemein formuliert)

Der Name des Usecase Controllers wird wie folgt gebildet: <TueEtwas>Controller

Abgrenzung

Dieses Muster sollte nur zur Anwendung kommen, wenn zur Implementierung eines Services ein Domänenmodell verwendet wird.

Stärken und Schwächen

Stärken
  • Trennung von Technik (Aufruf von Diensten) und Steuerungslogik
  • Einfache Standard-Implementierung
  • Wiederverwendung
  • Berücksichtigt auch Massendatenverarbeitung
  • Einfach testbar
Schwächen

keine

Benutzte Muster

Cookie Consent mit Real Cookie Banner