Usecase Controller - Verknüpfung von Domänenmodell und Repository

Der Einstieg in Application Core erfolgt über einen Usecase Controller, der den Anwendungsfall im fachlichen Kontext kapselt. Application Core ist technologieneutral und bildet sämtliche Fachlichkeit ab. Der Usecase Controller orchestriert im Normalfall folgende Aktionen

  • lädt / speichert das Domänenmodell über das Repository
  • führt Operationen auf dem Domänemodell aus
  • [optional] initiiert das Auslösen von Domain Events
  • ruft ggf. weitere Services auf
  • löst ggf. Business Events aus
Überblick über Application Core

Der Begriff Usecase Controller ist ein historischer Begriff. Er ist identisch mit den sogenannten Begriff des Application Service in DDD. Wichtig ist, dass die hier implementierte Logik technologieneutral ist und somit eine hohe Stabilität hinsichtlich von Änderungen aufweist. Application Services beinhalten keine fachliche Logik!

Dabei werden also Daten der eigenen Applikation über das Repository geladen. Daten, die nicht der Verantwortlichkeit der eigenen Applikation obliegen, können auch über fremde Services über Gateways gelesen werden.

Der Usecase Controller stößt demnach die Domänenlogik an, wobei die eigentliche Logik im Domänenmodell implementiert wird. Da Usecase Controller dem Application Core zugeordnet werden und intern Domänenlogik anstoßen, sollten auch hier die Begrifflichkeiten der Ubiquitous Language verwendet werden. Daher heißt der Controller im Beispiel auch BuecherAusleihen.

Usecase Controller haben folgende Namenskonvention:

<TueEtwas>Controller

Der nachfolgende Code zeigt exemplarisch einen Usecase Controller für das Ausleihen von Büchern:

public class BuecherAusleihenController: IBuecherAusleihenController
{
  private readonly IReadOnlyBenutzerkontoRepository benutzerkontoRepository;
  private readonly IReadOnlyBuchRepository buchRepository;
  private readonly IAusleiheRepository ausleiheRepository;

  public BuecherAusleihenController(
    IReadOnlyBenutzerkontoRepository benutzerkontoRepository,
    IReadOnlyBuchRepository buchRepository,
    IAusleiheRepository ausleiheRepository)
  {
    this.benutzerkontoRepository = benutzerkontoRepository ?? throw new ArgumentNullException(nameof(benutzerkontoRepository));
    this.buchRepository = buchRepository ?? throw new ArgumentNullException(nameof(buchRepository));
    this.ausleiheRepository = ausleiheRepository ?? throw new ArgumentNullException(nameof(ausleiheRepository));
  }

  public async Task<IAusleihen> LeiheAusAsync(Sid benutzerSid,IEnumerable<BuchId> buecherIds)
  {
    if(benutzerSid == null) { throw new ArgumentNullException(nameof(benutzerSid)); }
    if(buecherIds == null) { throw new ArgumentNullException(nameof(buecherIds)); }

    Benutzerkonto benutzerkonto = await QueryBenutzerkontoAsync(benutzerSid);
    IBuecher auszuleihendeBuecher = await QueryBuecherAsync(buecherIds);
    IAusleihen ausleihen = benutzerkonto.LeiheAus(auszuleihendeBuecher);
    ausleiheRepository.Add(ausleihen);
    ValidationResult validationResult = ValidateRegardingLeiheAus(benutzerkonto,ausleihen,auszuleihendeBuecher);
    validationResult.ThrowIfInvalid();
    await ausleiheRepository.FlushAsync(); // Löst Domain Events aus

    return ausleihen;
  }
  ...
}

Im Allgemeinen soll genau ein Aggregate pro Operation des Usecase Controllers geändert werden, wobei im Allgemeinen aber auch Informationen und Logik aus anderen Aggregaten benötigt wird. Um dies im Code besser erkennen zu können, implementiert das Repository, das Aggregate nur für den lesenden Zugriff liefert eine sogenannte Readonly-Schnittstelle, z.B. IReadOnlyBuchRepository:

publik Interface IReadOnlyBuchRepository
{
    IBuecher QueryById(IEnumerable<BuchId> buchIds);
    ...
}

public interface IBuchRepository : IReadOnlyBuchRepository
{
    IBuecher Add(IBuecher buecher);
    ...
}

Änderungen an mehreren Aggregaten werden über Domain Events und Business Events propagiert. Im obigen Code erfolgt diese über eine entsprechende Konfiguration des Repositorys. Wir werden hierauf im folgenden Abschnitt genauer eingehen.

Cookie Consent mit Real Cookie Banner