Domain-Driven-Design - Externe Abhängigkeiten im Entity-Problem

22

Ich möchte Domain-Driven-Design starten, aber es gibt einige Probleme, die ich lösen möchte, bevor ich anfange :)

Stellen wir uns vor, ich habe Gruppen und Benutzer, und wenn der Benutzer einer Gruppe beitreten möchte, rufe ich die groupsService.AddUserToGroup(group, user)Methode auf. In DDD sollte ich tun group.JoinUser(user), was ziemlich gut aussieht.

Das Problem tritt auf, wenn Validierungsregeln zum Hinzufügen eines Benutzers vorhanden sind oder einige externe Aufgaben gestartet werden müssen, wenn Benutzer zur Gruppe hinzugefügt werden. Wenn Sie diese Aufgaben ausführen, hat die Entität externe Abhängigkeiten.

Ein Beispiel könnte sein - eine Einschränkung, dass Benutzer nur an maximal 3 Gruppen teilnehmen dürfen. Dies erfordert DB-Aufrufe von der Methode group.JoinUser, um dies zu überprüfen.

Aber die Tatsache, dass eine Entität von einigen externen Diensten / Klassen abhängt, scheint mir nicht so gut und "natürlich" zu sein.

Wie gehe ich mit DDD richtig um?

Shaddix
quelle

Antworten:

15

Stellen wir uns vor, ich habe eine Groups and Users-Methode, und wenn der Benutzer einer Gruppe beitreten möchte, rufe ich die groupsService.AddUserToGroup-Methode (group, user) auf. In DDD sollte ich group.JoinUser (user) machen, was ziemlich gut aussieht.

DDD empfiehlt Ihnen jedoch auch, (zustandslose) Dienste zum Ausführen von Aufgaben zu verwenden, wenn die jeweilige Aufgabe zu komplex ist oder nicht in ein Entitätsmodell passt. Es ist in Ordnung, Dienste in der Domänenschicht zu haben. Die Dienste in der Domänenschicht sollten jedoch nur Geschäftslogik enthalten. Externe Aufgaben und Anwendungslogik (wie das Senden einer E-Mail) sollten hingegen den Domänendienst in der Anwendungsschicht verwenden, in den Sie beispielsweise einen separaten (Anwendungs-) Dienst einbinden können.

Das Problem tritt auf, wenn ich einige Validierungsregeln zum Hinzufügen eines Benutzers habe ...

Die Validierungsregeln gehören zum Domain-Modell! Sie sollten in den Domänenobjekten (Entitäten usw.) gekapselt sein.

... oder einige externe Aufgaben müssen gestartet werden, wenn Benutzer zur Gruppe hinzugefügt werden. Wenn Sie diese Aufgaben ausführen, hat die Entität externe Abhängigkeiten.

Ich weiß zwar nicht, um welche externen Aufgaben es sich handelt, aber ich gehe davon aus, dass es sich um das Senden einer E-Mail usw. handelt. Dies ist jedoch nicht wirklich Teil Ihres Domain-Modells. Es sollte in der Anwendungsebene liegen und dort imho gehandhabt werden. Sie können einen Dienst in Ihrer Anwendungsebene haben, der auf Domänendiensten und Entitäten zum Ausführen dieser Aufgaben ausgeführt wird.

Aber die Tatsache, dass eine Entität von einigen externen Diensten / Klassen abhängt, scheint mir nicht so gut und "natürlich" zu sein.

Es ist unnatürlich und sollte nicht passieren. Das Unternehmen sollte nichts über Dinge wissen, die nicht in seiner Verantwortung liegen. Services sollten zum Orchestrieren von Entitätsinteraktionen verwendet werden.

Wie gehe ich mit DDD richtig um?

In Ihrem Fall sollte die Beziehung wahrscheinlich bidirektional sein. Ob der Benutzer der Gruppe beitritt oder die Gruppe den Benutzer übernimmt, hängt von Ihrer Domain ab. Tritt der Benutzer der Gruppe bei? Oder wird der Benutzer einer Gruppe hinzugefügt? Wie funktioniert es in Ihrer Domain?

Auf jeden Fall haben Sie eine bidirektionale Beziehung und können so die Anzahl der Gruppen bestimmen, zu denen der Benutzer bereits im Benutzeraggregat gehört. Ob Sie den Benutzer an die Gruppe oder die Gruppe an den Benutzer übergeben, ist technisch unbedeutend, sobald Sie die verantwortliche Klasse festgelegt haben.

Die Validierung sollte dann von der Entität durchgeführt werden. Das Ganze wird von einem Dienst der Anwendungsebene aufgerufen, der auch technische Dinge erledigen kann, wie das Versenden von E-Mails usw.

Wenn die Überprüfungslogik jedoch sehr komplex ist, ist ein Domänendienst möglicherweise die bessere Lösung. In diesem Fall kapseln Sie die Geschäftsregeln ein und rufen Sie sie dann von Ihrer Anwendungsebene aus auf.

Falke
quelle
Aber wenn wir so viel Logik außerhalb der Entität bewegen, was sollte drinnen bleiben?
SiberianGuy
Die direkten Verantwortlichkeiten des Unternehmens! Wenn Sie beispielsweise "Der Benutzer kann einer Gruppe beitreten" sagen, liegt dies in der Verantwortung der Benutzerentität. Manchmal muss man aus technischen Gründen Kompromissentscheidungen treffen. Ich bin auch kein großer Fan von bidirektionalen Beziehungen, aber manchmal passt es am besten zum Modell. Hören Sie also gut zu, wenn Sie über die Domain sprechen. "Eine Entität tut ..." "Die Entität kann ..." Wenn Sie solche Sätze hören, gehören diese Operationen höchstwahrscheinlich zur Entität.
Falcon
Außerdem wissen Sie, dass Sie einen Service benötigen, wenn zwei oder mehr nicht miteinander verbundene Objekte an einer Aufgabe teilnehmen müssen, um etwas zu erreichen.
Falcon
1
Danke für deine Antwort, Falcon! Übrigens habe ich immer versucht, zustandslose Dienste zu verwenden, also bin ich DDD einen Schritt näher gekommen :) Nehmen wir an, dass in einer Domäne dieser UserJoinsToGroup-Vorgang zu Group gehört. Das Problem ist, dass ich zur Validierung dieses Vorgangs wissen muss, an wie vielen Gruppen der Benutzer bereits teilnimmt (um einen Vorgang abzulehnen, wenn er bereits> 3 ist). Um zu wissen, dass ich die Datenbank abfragen muss. Wie kann ich das von der Konzerneinheit aus tun? Ich habe noch einige Beispiele, wenn ich die Datenbank in den Vorgängen berühren muss, die natürlich zur Entität gehören sollten (ich werde sie bei Bedarf veröffentlichen :))
Shaddix
2
Nun, wenn ich darüber nachdenke: Was ist mit einer GroupMembership-Entität? Es kann von einer Fabrik gebaut werden und diese Fabrik kann auf die Speicher zugreifen. Das wäre gut DDD und kapselt die Schaffung von Mitgliedschaften. Die Factory kann auf Repositorys zugreifen, eine Mitgliedschaft erstellen und diese dem Benutzer bzw. der Gruppe hinzufügen. Diese neue Entität könnte auch Berechtigungen einschließen. Vielleicht ist das eine gute Idee.
Falcon
3

Ich würde das Problem der Validierung folgendermaßen angehen: Erstellen Sie einen Domänendienst mit dem Namen MembershipService:

class MembershipService : IMembershipService
{
   public MembershipService(IGroupRepository groupRepository)
   { 
     _groupRepository = groupRepository;
   }
   public int NumberOfGroupsAssignedTo(UserId userId)
   {
        return _groupsRepository.NumberOfGroupsAssignedTo(userId);
   }
}

Der Konzerneinheit muss injiziert werden IMemberShipService. Dies kann auf Klassen- oder Methodenebene erfolgen. Nehmen wir an, wir machen das auf Methodenebene.

class Group{

   public void JoinUser(User user, IMembershipService membershipService)
   {
       if(membershipService.NumberOfGroupsAssignedTo(user.UserId) >= 3)
         throw new BusinessException("User assigned to more than 3 groups. Cannot proceed");

       // do some more stuff
   }
}

Der Application-Service: GroupServicekann mit der IMemberShipServiceConstructor-Injection injiziert werden, die dann an die JoinUserMethode der GroupKlasse übergeben werden kann.

Eklavya Gupta
quelle
1
Vielleicht möchten Sie den Quellcode in Ihrem Beitrag zur besseren Lesbarkeit formatieren
Benni