Frage

Ich suche nach Ratschlägen, wie sehr ich mich darum kümmern sollte, das Modell des anämischen Domänens zu vermeiden. Wir beginnen gerade mit DDD und kämpfen mit einer Analyse -Lähmung in Bezug auf einfache Entwurfsentscheidungen. Der jüngste Punkt, an dem wir festhalten, ist der Ort, an dem bestimmte Geschäftslogik gehört, zum Beispiel haben wir eine Order Objekt, das Eigenschaften hat wie Status usw. Sagen Sie jetzt, ich muss einen Befehl wie wie UndoLastStatus Weil jemand mit einer Bestellung einen Fehler gemacht hat, ist dies nicht so einfach wie das Ändern der Status Da andere Informationen protokolliert und Eigenschaften geändert werden müssen. Jetzt in der realen Welt ist dies eine reine Verwaltungsaufgabe. So wie ich es sehe, habe ich zwei Optionen, an die ich denken kann:

  • Option 1: Fügen Sie die Methode zum Bestellen hinzu, so etwas wie Order.UndoLastStatus(), Obwohl dies ein bisschen sinnvoll ist, spiegelt es die Domäne nicht wirklich wider. Ebenfalls Order ist das primäre Objekt im System und wenn alles, was die Bestellung beinhaltet, in die Auftragsklasse aufgenommen wird, könnte Dinge außer Kontrolle geraten.

  • Option 2: Erstellen a Shop Objekt und damit haben unterschiedliche Dienste, die unterschiedliche Rollen darstellen. Also könnte ich haben Shop.AdminService, Shop.DispatchService, und Shop.InventoryService. In diesem Fall hätte ich also Shop.AdminService.UndoLastStatus(Order).

Jetzt haben wir etwas, das die Domain viel mehr widerspiegelt, und es den Entwicklern ermöglichen, mit Geschäftsexperten über ähnliche Rollen zu sprechen, die tatsächlich vorhanden sind. Aber es geht auch zu einem anämischen Modell. Welches wäre der bessere Weg im Allgemeinen?

War es hilfreich?

Lösung

Option 2 würde sicher zu einem Verfahrenscode führen.
Könnte leichter zu entwickeln sein, aber viel schwerer zu pflegen.

Jetzt in der realen Welt ist dies eine reine Verwaltungsaufgabe

"Administration" -Tasks sollten privat und durch öffentliche, vollständige "Domain`ish" -Aktionen aufgerufen werden. Vorzugsweise - immer noch in einem leicht verständlichen Code geschrieben, der aus der Domäne gesteuert wird.

Wie ich es sehe - Problem ist das UndoLastStatus macht den Domain -Experten wenig Sinn.
Wahrscheinlicher ist, dass sie über das Erstellen, Stornieren und Ausfüllen von Bestellungen sprechen.

Etwas in dieser Richtung könnte besser passen:

class Order{
  void CancelOrder(){
    Status=Status.Canceled;
  }
  void FillOrder(){
    if(Status==Status.Canceled)
      throw Exception();
    Status=Status.Filled;
  }
  static void Make(){
    return new Order();
  }
  void Order(){
    Status=Status.Pending;
  }
}

Ich persönlich mag die Verwendung von "Status" nicht, sie werden automatisch an alles weitergegeben, was sie verwendet - ich sehe das als unnötige Kopplung.

Also würde ich so etwas haben:

class Order{
  void CancelOrder(){
    IsCanceled=true;
  }
  void FillOrder(){
    if(IsCanceled) throw Exception();
    IsFilled=true;
  }
  static Order Make(){
    return new Order();
  }
  void Order(){
    IsPending=true;
  }
}

Um verwandte Dinge zu ändern, wenn sich der Bestellstatus ändert, ist die beste Wette, so genannt zu verwenden Domänenereignisse.
Mein Code würde in diese Zeilen nachsehen:

class Order{
  void CancelOrder(){
    IsCanceled=true;
    Raise(new Canceled(this));
  }
  //usage of nested classes for events is my homemade convention
  class Canceled:Event<Order>{
    void Canceled(Order order):base(order){}
  }     
}

class Customer{
  private void BeHappy(){
    Console.WriteLine("hooraay!");
  }
  //nb: nested class can see privates of Customer
  class OnOrderCanceled:IEventHandler<Order.Canceled>{
   void Handle(Order.Canceled e){
    //caveat: this approach needs order->customer association
    var order=e.Source;
    order.Customer.BeHappy();
   }
  }
}

Wenn die Bestellung zu groß wächst, möchten Sie vielleicht herausfinden, was begrenzte Kontexte sind (wie Eric Evans sagt - wenn er die Chance hätte, sein Buch erneut zu schreiben, würde er begrenzte Kontexte bis zum Anfang bewegen).

Kurz gesagt - es ist eine Form der Zersetzung, die von Domäne angetrieben wird.

Idee ist relativ einfach - es ist in Ordnung, mehrere Bestellungen aus verschiedenen Sichtweisen zu haben, auch bekannt als Kontexte.

ZB - Bestellung aus dem Einkaufskontext, bestellen Sie aus dem Buchhaltungskontext.

namespace Shopping{
 class Order{
  //association with shopping cart
  //might be vital for shopping but completely irrelevant for accounting
  ShoppingCart Cart;
 }
}
namespace Accounting{
 class Order{
  //something specific only to accounting
 }
}

Aber normalerweise vermeidet genug Domain selbst Komplexität und ist leicht zu zerlegt, wenn Sie sie genau genug anhören. ZB, wie Sie möglicherweise von Experten -Begriffen wie OrderLifecycle, OrderHistory und OrderDescription hören, die Sie als Anker für die Zersetzung nutzen können.

NB: Denken Sie daran - ich habe kein Verständnis für Ihre Domain.
Es ist sehr wahrscheinlich, dass diese Verben, die ich benutze, völlig seltsam sind.

Andere Tipps

Ich würde von der geleitet sein FASSEN Prinzipien. Wende an Informationsexperte Designprinzip, das heißt, Sie sollten der Klasse die Verantwortung zuweisen, die natürlich die meisten Informationen enthält, die erforderlich sind, um die Änderung zu erfüllen.

In diesem Fall würde ich, da das Ändern des Bestellstatus andere Entitäten betrifft, jede dieser Domänenobjekte auf niedriger Ebene eine Methode unterstützen, um die Änderung in Bezug auf sich selbst anzuwenden. Verwenden Sie dann auch eine Domänendienstebene, wie Sie in Option 2 beschrieben werden, die den gesamten Betrieb abstrahiert und mehrere Domänenobjekte nach Bedarf absperrt.

Siehe auch die Fassade Muster.

Ich denke, eine Methode wie Unbelebung in der Order -Klasse fühlt sich ein bisschen falsch an, weil die Gründe für ihre Existenz außerhalb des Rahmens eines Ordens in gewissem Sinne liegen. Andererseits passt eine Methode, die für die Änderung des Status einer Bestellung verantwortlich ist. Der Status einer Bestellung ist ein ordnungsgemäßes Domänenkonzept und die Änderung dieses Status sollte über die Auftragsklasse erfolgen, da sie die mit einem Bestellstatus verbundenen Daten besitzt - es liegt in der Verantwortung der Auftragsklasse, sich selbst und in einem ordnungsgemäßen Zustand zu halten .

Eine andere Möglichkeit, darüber nachzudenken, ist, dass das Auftragsobjekt das, was in der Datenbank bestehen bleibt, und es ist der "letzte Stopp" für alle Änderungen, die auf eine Bestellung angewendet werden. Es ist einfacher zu argumentieren, was ein gültiger Zustand für eine Bestellung aus der Perspektive einer Ordnung und nicht aus der Perspektive einer externen Komponente sein könnte. Darum geht es bei DDD und OOP alles, was es dem Menschen leichter macht, über Code zu argumentieren. Darüber hinaus kann der Zugang zu privaten oder geschützten Mitgliedern erforderlich sein, um eine Zustandsänderung durchzuführen. In diesem Fall ist die Methode in der Bestellklasse eine bessere Option. Dies ist einer der Gründe, warum anämische Domänenmodelle verpönt sind - sie verlagern die Verantwortung, den Staat konsistent von der Besitzklasse fernzuhalten, wodurch unter anderem die Verkapselung verstößt.

Eine Möglichkeit, einen spezifischeren Betrieb wie Unbelebungstatus zu implementieren, besteht darin, einen Ordnerservice zu erstellen, der die Domäne aufdeckt und wie externe Komponenten auf der Domäne arbeiten. Dann können Sie ein einfaches Befehlsobjekt wie folgt erstellen:

class UndoLastStatusCommand {
  public Guid OrderId { get; set; }
}

A Der ordnungsservice hätte eine Methode, um diesen Befehl zu verarbeiten:

public void Process(UndoLastStatusCommand command) {
  using (var unitOfWork = UowManager.Start()) {
    var order = this.orderRepository.Get(command.OrderId);
    if (order == null)
      throw some exception

    // operate on domain to undo last status

    unitOfWork.Commit();
  }
}

Jetzt enthält das Domänenmodell für Ordnung alle Daten und Verhaltens externe Komponenten wie die Präsentationsschicht.

Überlegen Sie sich auch das Konzept von Domänenereignisse die anämische Domänenmodelle und Möglichkeiten zur Verbesserung betrachtet.

Es hört sich so an, als würden Sie diese Domäne nicht von Tests fahren. Schauen Sie sich die Arbeit von an Rob Vens, insbesondere seine Arbeit an der Erkundungsmodellierung, Zeitinversion und aktiv-passiven.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top