In der Debatte von Rich vs. Anemic Domain Models steckt das Internet voller philosophischer Ratschläge, aber es fehlen maßgebliche Beispiele. Ziel dieser Frage ist es, definitive Richtlinien und konkrete Beispiele für geeignete domänengetriebene Designmodelle zu finden. (Idealerweise in C #.)
Für ein reales Beispiel scheint diese Implementierung von DDD falsch zu sein:
Bei den folgenden WorkItem-Domänenmodellen handelt es sich nur um Eigenschaftensäcke, die von Entity Framework für eine Code-First-Datenbank verwendet werden. Per Fowler ist es anämisch .
Die WorkItemService-Schicht ist anscheinend eine häufige Fehleinschätzung von Domain Services. Es enthält die gesamte Verhaltens- / Geschäftslogik für das WorkItem. Per Yemelyanov und andere ist es prozedural . (S. 6)
Also, wenn das unten stehende falsch ist, wie kann ich es richtig machen?
Das Verhalten, dh AddStatusUpdate oder Checkout , sollte zur WorkItem-Klasse gehören.
Welche Abhängigkeiten sollte das WorkItem-Modell haben?
public class WorkItemService : IWorkItemService {
private IUnitOfWorkFactory _unitOfWorkFactory;
//using Unity for dependency injection
public WorkItemService(IUnitOfWorkFactory unitOfWorkFactory) {
_unitOfWorkFactory = unitOfWorkFactory;
}
public void AddStatusUpdate(int workItemId, int statusId) {
using (var unitOfWork = _unitOfWorkFactory.GetUnitOfWork<IWorkItemUnitOfWork>()) {
var workItemRepo = unitOfWork.WorkItemRepository;
var workItemStatusRepo = unitOfWork.WorkItemStatusRepository;
var workItem = workItemRepo.Read(wi => wi.Id == workItemId).FirstOrDefault();
if (workItem == null)
throw new ArgumentException(string.Format(@"The provided WorkItem Id '{0}' is not recognized", workItemId), "workItemId");
var status = workItemStatusRepo.Read(s => s.Id == statusId).FirstOrDefault();
if (status == null)
throw new ArgumentException(string.Format(@"The provided Status Id '{0}' is not recognized", statusId), "statusId");
workItem.StatusHistory.Add(status);
workItemRepo.Update(workItem);
unitOfWork.Save();
}
}
}
(Dieses Beispiel wurde vereinfacht, um besser lesbar zu sein. Der Code ist definitiv immer noch klobig, da es sich um einen verwirrten Versuch handelt. Das Verhalten der Domäne war jedoch: Aktualisierungsstatus durch Hinzufügen des neuen Status zum Archivverlauf. Letztendlich stimme ich den anderen Antworten zu könnte nur von CRUD gehandhabt werden.)
Aktualisieren
@AlexeyZimarev gab die beste Antwort, ein perfektes Video zu diesem Thema in C # von Jimmy Bogard, aber es wurde anscheinend in einen Kommentar unten verschoben, weil es nicht genügend Informationen über den Link hinaus gab. Ich habe einen groben Entwurf meiner Notizen, die das Video in meiner Antwort unten zusammenfassen. Bitte kommentieren Sie die Antwort mit eventuellen Korrekturen. Das Video ist eine Stunde lang, aber sehr sehenswert.
Update - 2 Jahre später
Ich denke, es ist ein Zeichen für die beginnende Reife von DDD, dass ich auch nach 2-jährigem Studium nicht versprechen kann, dass ich den "richtigen Weg" dazu kenne. Allgegenwärtige Sprache, aggregierte Wurzeln und sein Ansatz für verhaltensorientiertes Design sind die wertvollen Beiträge von DDD für die Branche. Beharrlichkeit, Ignoranz und Event-Sourcing sorgen für Verwirrung, und ich denke, dass eine solche Philosophie sie von einer breiteren Akzeptanz abhält. Aber wenn ich diesen Code mit dem, was ich gelernt habe, noch einmal machen müsste, würde er ungefähr so aussehen:
Ich freue mich immer noch über Antworten auf diesen (sehr aktiven) Beitrag, der einen Code für bewährte Methoden für ein gültiges Domain-Modell enthält.
"I don't want to duplicate all my entities into DTOs simply because I don't need it and it violates DRY, and I also don't want my client application to take a dependency on EntityFramework.dll"
. "Entities" im Entity Framework-Jargon ist nicht dasselbe wie "Entities" im "Domain Model"Antworten:
Die hilfreichste Antwort wurde von Alexey Zimarev gegeben und erhielt mindestens 7 positive Stimmen, bevor ein Moderator sie in einen Kommentar unterhalb meiner ursprünglichen Frage verschob ....
Seine Antwort:
Ich habe einige Notizen gemacht, um das Video für mein Team zusammenzufassen und um in diesem Beitrag ein wenig mehr Details zu liefern. (Das Video ist eine Stunde lang, aber wirklich jede Minute wert, wenn Sie Zeit haben. Jimmy Bogard verdient viel Anerkennung für seine Erklärung.)
Bitte zögern Sie nicht, andere Punkte zu kommentieren, die Ihrer Meinung nach enthalten sein sollten, oder wenn Sie der Meinung sind, dass eine dieser Anmerkungen falsch ist. Versucht, so viel wie möglich direkt zu zitieren oder zu paraphrasieren.
quelle
Ihre Frage kann nicht beantwortet werden, da Ihr Beispiel falsch ist. Insbesondere, weil es kein Verhalten gibt. Zumindest nicht im Bereich Ihrer Domain. Das Beispiel einer
AddStatusUpdate
Methode ist keine Domänenlogik, sondern eine Logik, die diese Domäne verwendet. Diese Art von Logik ist sinnvoll, wenn Sie sich in einer Art Service befinden, der externe Anforderungen verarbeitet.Wenn zum Beispiel die Anforderung bestand, dass ein bestimmtes Arbeitselement nur bestimmte Status haben darf oder dass es nur N Status haben darf, dann ist dies Domänenlogik und sollte Teil einer
WorkItem
oderStatusHistory
als Methode sein.Der Grund für Ihre Verwirrung ist, dass Sie versuchen, eine Richtlinie auf Code anzuwenden, der sie nicht benötigt. Domänenmodelle sind nur relevant, wenn Sie viele komplexe Domänenlogiken haben. Z.B. Logik, die an Entitäten selbst arbeitet und sich aus Anforderungen ergibt. Wenn es im Code darum geht, Entitäten von außen zu manipulieren, handelt es sich höchstwahrscheinlich nicht um eine Domänenlogik. Aber in dem Moment, in dem Sie viele
if
s erhalten, basierend auf den Daten und Entitäten, mit denen Sie arbeiten, ist dies Domänenlogik.Eines der Probleme der echten Domänenmodellierung besteht darin, komplexe Anforderungen zu verwalten. Und als solches können seine wahre Kraft und Vorteile nicht in einfachem Code dargestellt werden. Sie benötigen Dutzende von Unternehmen mit einer Vielzahl von Anforderungen, um die Vorteile wirklich zu erkennen. Auch hier ist Ihr Beispiel zu einfach, als dass das Domain-Modell wirklich glänzen könnte.
Abschließend möchte ich noch erwähnen, dass ein echtes Domain-Modell mit echtem OOP-Design mit dem Entity Framework nur schwer zu erhalten ist. Während ORMs mit der Abbildung einer echten OOP-Struktur auf relationale entworfen wurden, gibt es immer noch viele Probleme, und das relationale Modell wird häufig in das OOP-Modell übernommen. Selbst mit nHibernate, das ich für viel leistungsfähiger als EF halte, kann dies ein Problem sein.
quelle
Ihre Annahme, dass das Einkapseln Ihrer mit WorkItem verbundenen Geschäftslogik in einen "Fat Service" ein inhärentes Anti-Pattern ist, das ich argumentieren würde, ist nicht unbedingt.
Unabhängig von Ihren Überlegungen zum anämischen Domänenmodell fördern die für eine Branchen-.NET-Anwendung typischen Standardmuster und -methoden einen mehrschichtigen Transaktionsansatz, der aus verschiedenen Komponenten besteht. Sie fördern die Trennung der Geschäftslogik vom Domänenmodell, um die Kommunikation eines gemeinsamen Domänenmodells zwischen anderen .NET-Komponenten sowie zwischen Komponenten auf verschiedenen Technologie-Stacks oder über physische Ebenen hinweg zu erleichtern.
Ein Beispiel hierfür ist ein .NET-basierter SOAP-Webdienst, der mit einer Silverlight-Clientanwendung kommuniziert, die zufällig eine DLL mit einfachen Datentypen enthält. Dieses Domänenentitätsprojekt kann in eine .NET-Assembly oder eine Silverlight-Assembly integriert werden, in der interessierte Silverlight-Komponenten mit dieser DLL keinem Objektverhalten ausgesetzt sind, das möglicherweise von Komponenten abhängt, die nur für den Dienst verfügbar sind.
Unabhängig von Ihrer Haltung zu dieser Debatte ist dies das von Microsoft angenommene und akzeptierte Muster, und meiner Meinung nach ist es kein falscher Ansatz, aber ein Objektmodell, das sein eigenes Verhalten definiert, ist auch nicht unbedingt ein Anti-Muster. Wenn Sie mit diesem Entwurf fortfahren, ist es am besten, einige der Einschränkungen und Schwachstellen zu erkennen und zu verstehen, auf die Sie stoßen könnten, wenn Sie andere Komponenten integrieren müssen, die Ihr Domänenmodell anzeigen müssen. In diesem speziellen Fall möchten Sie vielleicht, dass ein Übersetzer Ihr objektorientiertes Domänenmodell in einfache Datenobjekte konvertiert, die bestimmte Verhaltensmethoden nicht verfügbar machen.
quelle
Mir ist klar, dass diese Frage ziemlich alt ist, daher ist diese Antwort für die Nachwelt bestimmt. Ich möchte mit einem konkreten Beispiel antworten, anstatt mit einem theoretischen.
Kapsele die "Änderung des Workitem-Status" in der
WorkItem
Klasse wie folgt:Jetzt ist Ihre
WorkItem
Klasse dafür verantwortlich, sich in einem legalen Zustand zu halten. Die Implementierung ist jedoch ziemlich schwach. Der Product Owner möchte einen Verlauf aller Statusaktualisierungen, die an der vorgenommen wurdenWorkItem
.Wir ändern es in etwa so:
Die Implementierung hat sich drastisch geändert, aber der Aufrufer der
ChangeStatus
Methode kennt die zugrunde liegenden Implementierungsdetails nicht und hat keinen Grund, sich selbst zu ändern.Dies ist ein Beispiel für eine Rich Domain Model-Entität, IMHO.
quelle