Skip to content

Commit

Permalink
Update examples documentation (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
cottinisimone authored Jun 9, 2023
1 parent bf025ba commit 826ec06
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 10 deletions.
3 changes: 3 additions & 0 deletions examples/aggregate_deletion/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! The purpose of this example is to demonstrate the process of deleting an aggregate and its
//! projections using the [`PgStore`].
use sqlx::{Pool, Postgres};
use uuid::Uuid;

Expand Down
2 changes: 2 additions & 0 deletions examples/common/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Common structs shared between all the other examples
use rand::prelude::IteratorRandom;
use sqlx::postgres::PgPoolOptions;
use sqlx::{Pool, Postgres};
Expand Down
7 changes: 7 additions & 0 deletions examples/event_bus/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
//! This example contains the code that showcases the following steps:
//!
//! - Configuring the event bus with the necessary settings for the Kafka and RabbitMQ event bus implementations.
//! - Setting up event handlers and an aggregate manager to handle commands and publish events to the event bus.
//! - Handling a sample command using the aggregate manager, which triggers the publication of an event to the event bus.
//! - Observing the output to see the events being published to the respective event bus implementations.
use std::time::Duration;

use lapin::ExchangeKind;
Expand Down
4 changes: 4 additions & 0 deletions examples/eventual_view/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! In this example, we provide a demonstration of building a view using an [`EventHandler`]. The
//! example illustrates the process of handling events and processing them to construct a
//! comprehensive view of the underlying data.
use sqlx::{Pool, Postgres};
use uuid::Uuid;

Expand Down
35 changes: 35 additions & 0 deletions examples/locking_strategies/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
//! # Locking Strategies Example
//!
//! This example is divided into two sections, each focusing on different aspects of locking strategies:
//! Optimistic Concurrency Control (OCC) and Pessimistic Concurrency Control (PCC).
//!
//! ## Section 1: Optimistic and Pessimistic Concurrency Control
//! The first section demonstrates how to implement and utilize Optimistic Concurrency Control and
//! Pessimistic Concurrency Control in event sourcing scenarios.
//!
//! - Optimistic Concurrency Control (OCC):
//! This part showcases the usage of optimistic locks, which allow multiple threads to read and
//! modify data concurrently. The example illustrates how conflicts are detected during the write
//! operation by employing a versioning mechanism.
//!
//! - Pessimistic Concurrency Control (PCC):
//! In this section, the example demonstrates the application of pessimistic locks, which acquire
//! locks on data before performing modifications. It showcases how these locks ensure exclusive
//! access to data, preventing concurrent modifications and maintaining data integrity.
//!
//! ## Section 2: Showcase of Optimistic and Pessimistic Locks
//! The second section of the example provides a detailed showcase of how optimistic and pessimistic
//! locks function in practice.
//!
//! - Pessimistic Locking Showcase:
//! This part demonstrates the behavior of a pessimistic lock where one thread remains blocked
//! while another thread holds a lock on a specific aggregate state. The example employs guard
//! assertions to verify that the blocked thread remains suspended until the lock is released by
//! the other thread.
//!
//! - Optimistic Locking Showcase:
//! This section highlights the versatility of optimistic locking, even when a pessimistic lock is
//! already in progress. It demonstrates how optimistic locks can still be utilized effectively,
//! allowing other operations to proceed while ensuring conflicts are detected and resolved during
//! write operations.
use std::sync::Arc;
use std::time::Duration;

Expand Down
19 changes: 16 additions & 3 deletions examples/multi_aggregate_rebuild/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
//! This example serves as a demonstration of creating a custom script to rebuild two separate
//! [`Aggregate`]s that share the same view.
//!
//! The script streams events from two different event stores, opens a transaction, and truncates
//! both tables.
//!
//! It's important to note that the view handled by the `SharedEventHandler` is truncated without
//! using the transaction. This is because the view is eventually consistent and its deletion is
//! unlikely to cause an outage.
//!
//! After truncation, each event is passed to its respective handler in chronological order.
//! Finally, the transaction is committed.
use futures::StreamExt;
use sqlx::{PgConnection, Pool, Postgres, Transaction};
use uuid::Uuid;
Expand Down Expand Up @@ -67,9 +80,6 @@ async fn rebuild_multi_aggregate(
let mut event_a_opt: Option<Result<StoreEvent<EventA>, PgStoreError>> = events_a.next().await;
let mut event_b_opt: Option<Result<StoreEvent<EventB>, PgStoreError>> = events_b.next().await;

// At this point it's possible to open a transaction
let mut transaction: Transaction<Postgres> = pool.begin().await.unwrap();

// There are 3 choices here:
// - Truncate all the tables where the event handlers and transactional event handlers insist on.
// - Implement the EventHandler::delete and TransactionalEventHandler::delete functions
Expand All @@ -81,6 +91,9 @@ async fn rebuild_multi_aggregate(
let query: String = format!("TRUNCATE TABLE {}", view.table_name());
let _ = sqlx::query(query.as_str()).execute(&pool).await.unwrap();

// At this point it's possible to open a transaction
let mut transaction: Transaction<Postgres> = pool.begin().await.unwrap();

let query: String = format!("TRUNCATE TABLE {}", transactional_view.table_name());
let _ = sqlx::query(query.as_str()).execute(&mut *transaction).await.unwrap();

Expand Down
16 changes: 9 additions & 7 deletions examples/rebuilder/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Basic example showing how to rebuild with two different strategies:
//! This basic example demonstrates how to rebuild data using two different strategies:
//!
//! - by aggregate id: the rebuild is made opening a transaction for every id fetched by a pre-made
//! query. In that transaction every row having a matching aggregate id is deleted and then
//! reprojected.
//! - Rebuilding by aggregate ID:
//! This strategy involves opening a transaction for each aggregate ID obtained through a
//! predefined query. Within each transaction, all rows with a matching aggregate ID are deleted,
//! and then the events are reprojected.
//!
//! - all at once: this is done in a transaction truncating all the table content and then rebuilding
//! all the events retrieved in the moment the transaction is opened.
//! - Rebuilding all at once:
//! In this strategy, a transaction is opened to truncate the entire table, removing all existing
//! content. Subsequently, all events retrieved at the time the transaction is initiated are rebuilt.
//!
//! Note that is not possible to rebuild using non-replayable event handlers.
//! Please note that rebuilding using non-replayable event handlers is not possible in this context.
//!
//! This will not compile:
//!
Expand Down
7 changes: 7 additions & 0 deletions examples/saga/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
//! This basic example showcases the usage of the saga pattern approach. It is worth noting that in
//! this particular scenario, an aggregate is employing a saga over itself, creating a form of
//! circular dependency.
//!
//! However, this circular dependency is effectively handled by encapsulating the [`PgStore`] within
//! an [`Arc`], ensuring atomicity and preventing issues.
use std::sync::Arc;

use futures::lock::Mutex;
Expand Down
2 changes: 2 additions & 0 deletions examples/shared_view/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! This example demonstrates how to construct a view that encompasses two distinct [`Aggregates`].
use sqlx::{Pool, Postgres};
use uuid::Uuid;

Expand Down
30 changes: 30 additions & 0 deletions examples/store_crud/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
//! In this example, we demonstrate various operations that can be performed on the event store.
//! These operations include:
//!
//! - Inserting an event into the store:
//! This operation allows you to add a new event to the event store, which will be associated with
//! a particular aggregate.
//!
//! - Inserting an event into the store with a given event ID:
//! Similar to the previous operation, this allows you to insert an event into the store, but with
//! a specific event ID specified by you.
//!
//! - Finding an event by event ID:
//! This operation enables you to retrieve a specific event from the event store by providing its
//! unique event ID.
//!
//! - Getting all events by aggregate ID:
//! With this operation, you can retrieve all events associated with a particular aggregate ID
//! from the event store.
//!
//! - Updating an event (payload) by event ID:
//! This operation allows you to update the payload or data associated with a specific event by
//! specifying its event ID.
//!
//! - Deleting an event by event ID:
//! This operation allows you to remove a specific event from the event store based on its event ID.
//!
//! - Deleting all aggregate events by aggregate ID:
//! This operation enables you to delete all events associated with a particular aggregate ID from
//! the event store.
use std::convert::TryInto;

use chrono::Utc;
Expand Down
16 changes: 16 additions & 0 deletions examples/transactional_view/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
//! This example showcases the process of constructing a view using a [`TransactionalEventHandler`].
//! The [`TransactionalEventHandler`] ensures strong consistency by executing all operations within
//! a transactional context. It guarantees that if a failure occurs during the execution of the
//! transaction, the event will not be persisted in the store, and an error will be returned to the
//! caller.
//!
//! By utilizing the `TransactionalEventHandler`, you can maintain a strong and reliable event
//! handling mechanism. The example emphasizes the importance of data integrity, as any failure in
//! the `TransactionalEventHandler` prevents the event from being stored, ensuring the consistency
//! of the view and the event store.
//!
//! This demonstration serves as a practical illustration of how the `TransactionalEventHandler`
//! helps enforce strong consistency in view construction. It highlights the reliability of the
//! approach by ensuring that events are either fully processed and persisted or not persisted at
//! all in the event of a failure, providing a clear and consistent state of the data.
use sqlx::{Pool, Postgres};
use uuid::Uuid;

Expand Down

0 comments on commit 826ec06

Please sign in to comment.