Alpha Version: You are viewing the ALPHA documentation. This is an experimental version and may contain breaking changes.
Skip to main content

Tutorials — Online Shop Example

A small, educational event-sourced application with two loosely-coupled Plugins — Catalog and Ordering — that integrate by ID through a versioned Extension Point protocol. The domain is familiar, maps cleanly onto event sourcing, and needs no saga, which makes it a good first example.

Plugin 1: Catalog

Manages the product catalogue — what is available for sale and how it is organized. Products are listings with a name, description, and price; Categories are named groupings products reference by ID.

Product commands and events

CommandEventWhat Happens
AddProductProductAddedRegisters a new product with its name, description, and price
ChangeProductNameProductNameChangedRenames an existing product
ChangeProductDescriptionProductDescriptionChangedUpdates a product's description
ChangeProductPriceProductPriceChangedChanges a product's price

Change* commands are idempotent — if the new value equals the current state, no event is written.

Category commands and events

CommandEventWhat Happens
AddCategoryCategoryAddedCreates a new named grouping
RenameCategoryCategoryRenamedRenames a category
ArchiveCategoryCategoryArchivedSoft-deletes a category

ProductDemand commands and events

ProductDemand is an internal entity — never commanded directly by clients. It is driven by events arriving from Ordering's Extension Point (see Cross-Plugin Integration).

CommandEventWhat Happens
RecordDemandProductDemandRecordedRecords that an active order references this product
RevokeDemandProductDemandRevokedRemoves the record when the order is cancelled

Plugin 2: Ordering

Handles the purchase flow. Customers are registered buyers; Orders are confirmed purchases referencing product IDs and a customer ID, with a linear lifecycle.

Customer commands and events

CommandEventWhat Happens
RegisterCustomerCustomerRegisteredCreates a new buyer account with name and contact details
UpdateEmailEmailUpdatedChanges the customer's email address
UpdateAddressAddressUpdatedChanges the customer's delivery address
DeactivateCustomerCustomerDeactivatedSuspends the account — deactivated customers cannot place orders

Order commands and events

CommandEventWhat Happens
PlaceOrderOrderPlacedCreates a new order referencing product IDs and a customer ID
ShipOrderOrderShippedMarks the order as dispatched — terminal success state
CancelOrderOrderCancelledCancels an order that has not yet shipped — terminal failure state

The order lifecycle is strictly linear: PlaceOrderShipOrder or CancelOrder; neither is reversible.

CatalogProduct (internal)

A lightweight shadow copy of Catalog product data that Ordering maintains so it can validate and display product information at order time without querying Catalog. Its SyncCatalogProduct command (event CatalogProductSynced) is driven by Catalog's Extension Point.

Automation and Integration

Beyond the core write/read patterns, the example demonstrates three reactive features. Each reuses an existing command or event — no domain-model changes are needed — and is implemented with a different component type per approach (see Choosing an approach):

  • Auto-Ship Order — when an order is placed, an automation issues ShipOrder. Aggregate: stateless Event Mappings; DCB: a stateful AutomationSlice.
  • Import Product from Supplier Feed — an anti-corruption layer validates external supplier data and publishes AddProduct. Aggregate: a file-triggered Task (CSV→S3); DCB: a webhook InboundTranslationSlice.
  • Send Order Confirmation Email — a notification on OrderPlaced. Aggregate: a fire-and-forget Side Effect; DCB: an OutboundTranslationSlice with per-item retry.

Cross-Plugin Integration

The two Plugins communicate only through IDs and a defined Extension Point protocol — neither imports the other's internal modules. (For how Extension Points and Extensions work, see the Plugin System guide.)

Product Catalog Sync (Catalog → Ordering). When Catalog adds or reprices a product it publishes ProductBecameAvailable / ProductPriceChanged through its ProductsExtensionPoint. Ordering's ProductsExtension dispatches SyncCatalogProduct to keep its shadow copy current.

Demand Tracking (Ordering → Catalog). When Ordering places or cancels an order it publishes ItemOrdered / ItemOrderCancelled (carrying productId and orderId) through its OrdersExtensionPoint. Catalog's OrdersExtension dispatches RecordDemand / RevokeDemand to maintain a per-product active-order count.

The Extension Point name is the only runtime coupling; the public event vocabulary is kept deliberately separate from internal events so refactoring never breaks the cross-plugin contract.

Implementations

The same domain is implemented three times — once per Reventless plugin style:

ImplementationPlugin StyleConsistencyBest For
Aggregate-BasedOne event log per aggregate instancePer aggregate instanceTraditional DDD, isolated entity lifecycles
DCB-BasedSingle shared event log with tag-filtered readsPer command (optimistic)Cross-entity consistency, simpler infrastructure
HybridAggregates for independent entities, DCB for interdependent onesPer entity typeBest of both

Each implementation is split into five packages: two spec packages holding only the public Extension Point contracts (catalog-spec, ordering-spec), two plugin packages with the internal logic (catalog, ordering), and one platform package that wires everything together. The spec packages are the only cross-plugin dependency.

Every plugin is a functor Make(Platform: ReventlessInfra.Platform.T) — swap ReventlessLocal for ReventlessAws to move from local development to production with no business-logic changes.


Next: Choosing an approach → — decide between Aggregate, DCB, and Hybrid, then follow the recommended walkthrough.