2013-07-20 5 views
6

Ich lese Vernons Artikel Effective Aggregate Design. Und ich habe eine Frage darüber, warum nur eine Sammelinstanz pro Transaktion geändert wird?Warum ändert nur eine Aggregat-Instanz pro Transaktion?

Nehmen wir ein Beispiel, betrachten Sie eine Lagerverwaltungs-Geschichte.

Inventar repräsentiert einen Artikel mit Menge in einem Lagerhaus. 5 Implementing Domain Driven Design Bücher in Shanghai Lager zum Beispiel.

Der Eintrag steht für ein Protokoll über einen Ein-/Aus-Vorgang an einem Inventory. Eingeben 2 Implementing Domain Driven Design Bücher in Shanghai Lager zum Beispiel.

Ein Lagerbestand Die Menge muss geändert werden, wenn ein Eintrag eingereicht wird.

Es kommt mir in den Sinn, dies ist eine Invariente könnte durch transaktionale Konsistenz implementiert werden.

Lösung A: Unter Verwendung eines Aggregate und Cluster Eintrag in Inventar.

public class Inventory implements Aggregate<Inventory> { 
    private InventoryIdentity id; 
    private Sku sku; 
    private int quantity; 
    private List<Entry> entries; 

    public void add(Entry entry) { 
     this.quantity += entry.getQuantity(); 
     this.entries.add(entry); 
    } 
} 

public class Entry implements LocalEntity<Entry> { 
    private int quantity; 
    // some other attributes such as whenSubmitted 
} 

public class TransactionalInventoryAdminService impelments InventoryAdminService, ApplicationService { 

    @Override 
    @Transactional 
    public void handle(InventoryIdentity inventoryId, int entryQuantity, ...other entry attributes) 
     Inventory inventory = inventoryRepository.findBy(inventoryId); 
     Entry entry = inventory.newEntry(entryQuantity, ..); 
     inventory.add(entry); 
     inventoryRepository.store(inventory); 
    } 
} 

Lösung B: Die Verwendung eigener Aggregate für Inventar und Eintrag.

public class Inventory implements Aggregate<Inventory> { 
    private InventoryIdentity id; 
    private Sku sku; 
    private int quantity; 

    public void add(int quantity) { 
     this.quantity += quantity; 
    } 
} 

public class Entry implements LocalEntity<Entry> { 
    private Inventory inventory; 
    private int quantity; 
    private boolean handled = false; 
    // some other attributes such as whenSubmitted 

    public void handle() { 
     if (handled) { 
      throw ..... 
     } else { 
      this.inverntory.add(quantity); 
      this.handled = true; 
     } 
    }  
} 

public class TransactionalInventoryAdminService impelments InventoryAdminService, ApplicationService { 

    @Override 
    @Transactional 
    public void handle(InventoryIdentity inventoryId, int entryQuantity, ...other entry attributes) 
     Inventory inventory = inventoryRepository.findBy(inventoryId); 
     Entry entry = inventory.newEntry(entryQuantity, ..); 
     entry.handle(); 
     inventoryRepository.store(inventory); 
     entryRepository.store(entry); 
    } 
} 

Sowohl A als auch B sind machbar, aber Lösung B ist eine Art unelegant für versehentliche oppertunity verlassen Inventar .Add (Anzahl) ohne Eintrag beteiligt aufzurufen. Ist das, was die Regel (ändert nur eine Aggregat-Instanz pro Transaktion) versucht, für mich zu zeigen? Ich bin meistens verwirrt, warum wir nur ein Aggregat in einer Transaktion ändern sollten, was schief geht, wenn wir das nicht tun.

Update1 starten

Ist es die Absicht, Concurrency Probleme zu lindern (mit einer anderen Regel "kleinere Aggregate machen")? Beispiel: Eintrag ist ein Aggregat mit relativ geringen Konflikten und Inventar ist einer mit relativ hoher Contotion (unter der Annahme, dass mehrere Benutzer einen Inventory manipulieren können), verursacht es unnötige Nebenläufigkeitsfehler, wenn ich sie beide in einer Transaktion ändern.

Update1 Ende

Einige weitere Probleme müssen angegangen werden, wenn ich die Lösung A annehmen:

1.Was passiert, wenn es viele Eintrag s für ein Inventar gibt und ich eine seitenbasierte Abfrage UI brauche? Wie implementiere ich eine ausgelagerte Abfrage mit Collections? Ein Weg ist, alle Eintrag s und wählt, was die Seite brauchen, die andere Möglichkeit ist InventoryRepository.findEntriesBy (Rechnungs-ID, Paging), aber dies scheint die Regel zu brechen eine lokale Entität nur zu bekommen, ist es aggregieren dann navigieren Sie die Objektgraph.

2.What, wenn es zu viele Eintrag s für ein Inventar und ich habe sie alle zu laden, wenn einen neuen Eintrag hinzufügen?

Ich weiß, dass diese Fragen aus mangelndem Verständnis stammen. Also jede Idee ist willkommen, danke im Voraus.

+0

Ich denke, dass dieser Ansatz mildert auch die Chance, dass ein Riegelschloss auftreten kann http://stackoverflow.com/questions/735894/database-deadlocks –

Antwort

10

Als Faustregel gilt, dass Sie Ihre Aggregate klein halten, da Transaktionsfehler aufgrund von Parallelität vermieden werden sollen. Und warum sollten wir den Speicher-Footprint groß machen, wenn es nicht sein sollte?

Also, Lösung A ist nicht optimal. Große Aggregate führen oft zu Problemen, die leicht vermieden werden können.

Es stimmt, dass eine weitere Faustregel darin besteht, nur ein Aggregat in einer Transaktion zu ändern. Wenn Sie den Eintrag "Entry" als eigenes Aggregat verwenden, können Sie die Menge des Inventars eventuell konsistent machen, was bedeutet, dass das Entry-Aggregat ein Ereignis auslösen kann, für das das Inventar abonniert ist. Auf diese Weise ändern Sie nur ein Aggregat pro Transaktion.

public class Entry { 
    public Entry(InventoryId inventoryId, int quantity) { 
     DomainEvents.Raise(new EntryAdded(inventoryId, quantity)) 
    } 
} 

Wenn Sie sich nicht wohl fühlen mit Eventual Consistency, können Sie immer noch halten die Aggregate trennen, aber jetzt sie beide in einer einzigen Transaktion ändern - bis Sie den Schmerz fühlen, eine Verkapselung von Domain-Service. Eine weitere Option besteht darin, die Domänenereignisse in Bearbeitung zu halten, sodass sie auch in einer einzelnen Transaktion festgeschrieben werden.

public class InventoryService { 
    public void AddEntryToInventory(Entry entry) { 
      // Modify Inventory quantity 
      // Add Entry 
    } 
} 
+0

+1 Vielen Dank für die Antwort. Ich bin meistens verwirrt, warum wir nur ein Aggregat in einer Transaktion ändern sollten, was schief geht, wenn wir das nicht tun. – Hippoom

+0

Ihre Erinnerung "Faustregel ist es, Ihre Aggregate klein zu halten" gab mir einen Hinweis, siehe meine Frage aktualisiert. – Hippoom

+0

Ich musste bei Google Übersetzer "lindern";) aber als Antwort auf dein Update: ja. – JefClaes