You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CQRS+ES架构事件存储的聚合表示、流定义及库存系统布局咨询

CQRS+ES for Inventory Management: Demystifying Event Streams & Aggregate Layouts

Hey there, let's break this down clearly—CQRS+ES can feel pretty abstract until you ground it in a concrete scenario like your inventory system. Let's start with your core question about event streams, then map everything directly to your use case.

First: What Exactly is an Event Stream in CQRS+ES?

At its core, an event stream is the complete, ordered history of every state change for a single aggregate instance. Aggregates are the transactional boundaries in Domain-Driven Design (DDD)—they encapsulate state and enforce business rules. So each stream is tied 1:1 to one aggregate instance, and the stream's ID is usually the aggregate's unique ID.

Why does this matter? It guarantees that all changes to an aggregate are sequential and isolated—no two concurrent commands can modify the same aggregate's state in a conflicting way, since events are appended to the stream in strict order.

Applying This to Your Inventory System

Your system tracks items with ID, productCode, and location attributes. Let's map this to CQRS+ES step by step:

Step 1: Define Your Aggregate Root

The core aggregate here is InventoryItem. Each individual item (with its unique ID) is an instance of this aggregate. This makes sense because each item's state (location, etc.) is independent—moving Item A doesn't affect Item B, even if they share the same productCode.

Step 2: Map Aggregates to Event Streams

Each InventoryItem instance gets its own dedicated event stream. The stream ID should be something like inventory-item-{itemId} (e.g., inventory-item-ITEM-001 for an item with ID ITEM-001).

Every state change to that item is appended as an event to its stream. For your attributes, typical events might include:

  • InventoryItemCreated: Fired when the item is added to inventory (includes itemId, productCode, initial location)
  • InventoryItemMoved: Fired when the item's location changes (includes itemId, newLocation, timestamp)
  • (Optional) InventoryItemDecommissioned: Fired if the item is removed from inventory

Example Event Stream for ITEM-001

Here's what the stream might look like (formatted as JSON events):

[
  {
    "eventType": "InventoryItemCreated",
    "itemId": "ITEM-001",
    "productCode": "PROD-WIDGET",
    "location": "WAREHOUSE-NORTH",
    "timestamp": "2024-05-01T09:00:00"
  },
  {
    "eventType": "InventoryItemMoved",
    "itemId": "ITEM-001",
    "newLocation": "STAGING-AREA-3",
    "timestamp": "2024-05-02T14:30:00"
  },
  {
    "eventType": "InventoryItemMoved",
    "itemId": "ITEM-001",
    "newLocation": "SHIPPING-BAY-1",
    "timestamp": "2024-05-03T10:15:00"
  }
]

Step 3: Clarify Common Confusions

You might be wondering: What about grouping by product code or location? Let's clear that up:

  • Don't create streams for product codes: A product code represents a type of item, not an individual transactional boundary. Multiple InventoryItem instances can share the same product code, but each has its own state. If you grouped by product code, you'd lose the ability to track individual items' histories and risk concurrent modification conflicts.
  • Don't create streams for locations: Location is an attribute of an InventoryItem, not an aggregate root. Many items can be in the same location, but their state changes are independent.

Step 4: Visualizing the System Layout

Let's simplify the overall structure:

Write Side (Command Model)

  • Command Handlers: Receive commands like CreateInventoryItem or MoveInventoryItem, load the corresponding InventoryItem aggregate by replaying its event stream, apply the command (if business rules are satisfied), and append the new event to the stream.
  • Event Store: Stores all streams, indexed by stream ID. Each stream is an ordered list of events for one InventoryItem.

Read Side (Query Model)

Since CQRS separates writes and reads, you'll build read models optimized for your queries. For example:

  • InventoryItemSummaryView: A flat view with itemId, productCode, currentLocation, lastUpdated—built by listening to all InventoryItem events and updating the view whenever an event is emitted.
  • ProductLocationAggregateView: A view that groups items by productCode and shows how many are in each location—useful for reporting.

Key Takeaways

  • Event streams = single aggregate instance state history
  • Aggregate root ID = stream ID (1:1 mapping)
  • In your inventory system, every unique item gets its own stream
  • Read models are built independently from streams to answer specific queries without impacting write performance

内容的提问来源于stack exchange,提问作者AMG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:07:33