Wo ziehen wir die Grenze zwischen Delegation und Kapselung von Geschäftslogik? Mir scheint, je mehr wir delegieren, desto anämischer werden wir. Die Delegation fördert jedoch auch die Wiederverwendung und den DRY-Prinzipal. Was ist also angemessen, um zu delegieren, und was sollte in unseren Domänenmodellen verbleiben?
Nehmen Sie die folgenden Bedenken als Beispiele:
Autorisierung . Soll das Domänenobjekt für die Aufrechterhaltung seiner Zugriffssteuerungsregeln verantwortlich sein (z. B. eine CanEdit-Eigenschaft) oder sollte diese an eine andere Komponente / einen anderen Dienst delegiert werden, die / der ausschließlich für die Zugriffsverwaltung verantwortlich ist, z. B. IAuthorizationService.CanEdit (Objekt)? Oder sollte es eine Kombination aus beidem sein? Hat das Domänenobjekt möglicherweise eine CanEdit-Eigenschaft, die an einen internen IAuthorizationService delegiert wird, um die eigentliche Arbeit auszuführen?
Validierung . Die gleiche Diskussion wie oben bezieht sich auf die Validierung. Wer hält die Regeln ein und wer ist für deren Bewertung verantwortlich? Einerseits sollte der Status des Objekts zu diesem Objekt gehören, und die Gültigkeit ist ein Status, aber wir möchten nicht den Code neu schreiben, der zum Auswerten von Regeln für jedes Domänenobjekt verwendet wird. Wir könnten Vererbung in diesem Fall verwenden ...
Objekterstellung . Factory-Klasse versus Factory-Methoden versus 'Neuanlegen' einer Instanz. Wenn wir eine separate Factory-Klasse verwenden, können wir die Erstellungslogik isolieren und kapseln, allerdings auf Kosten des Öffnens des Status unseres Objekts für die Factory. Dies kann verwaltet werden, wenn sich unsere Domänenschicht in einer separaten Assembly befindet, indem ein interner Konstruktor verfügbar gemacht wird, der von der Factory verwendet wird. Dies wird jedoch zu einem Problem, wenn mehrere Erstellungsmuster vorhanden sind. Und wenn die Fabrik nur den richtigen Konstrukteur anruft, was bringt es dann, die Fabrik zu haben?
Factory-Methoden für die Klasse beseitigen das Problem mit dem Öffnen des internen Zustands des Objekts. Da sie jedoch statisch sind, können wir Abhängigkeiten nicht durch Einfügen einer Factory-Schnittstelle auflösen, wie dies mit einer separaten Factory-Klasse möglich ist.
Ausdauer . Man könnte argumentieren, dass, wenn unser Domain-Objekt CanEdit verfügbar macht und gleichzeitig die Verantwortung für die Berechtigungsprüfung an eine andere Partei delegiert (IAuthorizationService), eine Save-Methode für unser Domain-Objekt vorhanden ist, die dasselbe tut. Auf diese Weise können wir den internen Zustand des Objekts auswerten, um festzustellen, ob die Operation ohne Unterbrechung der Kapselung ausgeführt werden kann. Natürlich müssen wir die Repository-Instanz in unser Domain-Objekt einfügen, was für mich ein wenig riecht. Lösen wir stattdessen ein Domain-Ereignis aus und gestatten einem Handler, die Persistenzoperation auszuführen?
Sehen Sie, wohin ich damit gehe?
Rockford Lhotka hat eine großartige Diskussion über seine Gründe, die Class-in-Charge-Methode für sein CSLA-Framework zu wählen. Ich habe ein wenig Erfahrung mit diesem Framework und kann seine Vorstellung von Geschäftsobjekten in vielerlei Hinsicht als Parallele zu Domänenobjekten sehen. Aber ich frage mich, ob die Zusammenarbeit zu viel wird, wenn ich versuche, mich an gute DDD-Ideale zu halten.
Wenn ich einen IAuthorizationService, einen IValidator, eine IFactory und ein IRepository für mein Gesamtstammverzeichnis habe, was bleibt dann übrig? Hat eine Publish-Methode, die den Status des Objekts von Draft in Published ändert, genug, um die Klasse als nicht anämisches Domänenobjekt zu betrachten?
Ihre Gedanken?