Domain modeling. Event sourcing. CQRS.

Overview

f(model) - Functional Domain Modeling with Rust

Publicly available at crates.io and docs.rs

When you’re developing an information system to automate the activities of the business, you are modeling the business. The abstractions that you design, the behaviors that you implement, and the UI interactions that you build all reflect the business — together, they constitute the model of the domain.

event-modeling

IOR<Library, Inspiration>

This project can be used as a library, or as an inspiration, or both. It provides just enough tactical Domain-Driven Design patterns, optimised for Event Sourcing and CQRS.

Abstraction and generalization

Abstractions can hide irrelevant details and use names to reference objects. It emphasizes what an object is or does rather than how it is represented or how it works.

Generalization reduces complexity by replacing multiple entities which perform similar functions with a single construct.

Abstraction and generalization are often used together. Abstracts are generalized through parameterization to provide more excellent utility.

Box<dyn Fn(&C, &S) -> Vec<E>>

type DecideFunction<'a, C, S, E> = Box<dyn Fn(&C, &S) -> Vec<E> + 'a + Send + Sync>

On a higher level of abstraction, any information system is responsible for handling the intent (Command) and based on the current State, produce new facts (Events):

  • given the current State/S on the input,
  • when Command/C is handled on the input,
  • expect Vec of new Events/E to be published/emitted on the output

Box<dyn Fn(&S, &E) -> S>

type EvolveFunction<'a, S, E> = Box<dyn Fn(&S, &E) -> S + 'a + Send + Sync>

The new state is always evolved out of the current state S and the current event E:

  • given the current State/S on the input,
  • when Event/E is handled on the input,
  • expect new State/S to be published on the output

Two functions are wrapped in a datatype class (algebraic data structure), which is generalized with three generic parameters:

pub struct Decider<'a, C: 'a, S: 'a, E: 'a> {
    pub decide: DecideFunction<'a, C, S, E>,
    pub evolve: EvolveFunction<'a, S, E>,
    pub initial_state: InitialStateFunction<'a, S>,
}

Decider is the most important datatype, but it is not the only one. There are others:

onion architecture image

Decider

Decider is a datatype/struct that represents the main decision-making algorithm. It belongs to the Domain layer. It has three generic parameters C, S, E , representing the type of the values that Decider may contain or use. Decider can be specialized for any type C or S or E because these types do not affect its behavior. Decider behaves the same for C=Int or C=YourCustomType, for example.

Decider is a pure domain component.

  • C - Command
  • S - State
  • E - Event
pub type DecideFunction<'a, C, S, E> = Box<dyn Fn(&C, &S) -> Vec<E> + 'a + Send + Sync>;
pub type EvolveFunction<'a, S, E> = Box<dyn Fn(&S, &E) -> S + 'a + Send + Sync>;
pub type InitialStateFunction<'a, S> = Box<dyn Fn() -> S + 'a + Send + Sync>;

pub struct Decider<'a, C: 'a, S: 'a, E: 'a> {
    pub decide: DecideFunction<'a, C, S, E>,
    pub evolve: EvolveFunction<'a, S, E>,
    pub initial_state: InitialStateFunction<'a, S>,
}

Additionally, initialState of the Decider is introduced to gain more control over the initial state of the Decider.

Event-sourcing aggregate

Event sourcing aggregate is using/delegating a Decider to handle commands and produce new events. It belongs to the Application layer. In order to handle the command, aggregate needs to fetch the current state (represented as a list/vector of events) via EventRepository.fetchEvents async function, and then delegate the command to the decider which can produce new events as a result. Produced events are then stored via EventRepository.save async function.

It is a formalization of the event sourced information system.

State-stored aggregate

State stored aggregate is using/delegating a Decider to handle commands and produce new state. It belongs to the Application layer. In order to handle the command, aggregate needs to fetch the current state via StateRepository.fetchState async function first, and then delegate the command to the decider which can produce new state as a result. New state is then stored via StateRepository.save async function.

It is a formalization of the state stored information system.

View

View is a datatype that represents the event handling algorithm, responsible for translating the events into denormalized state, which is more adequate for querying. It belongs to the Domain layer. It is usually used to create the view/query side of the CQRS pattern. Obviously, the command side of the CQRS is usually event-sourced aggregate.

It has two generic parameters S, E, representing the type of the values that View may contain or use. View can be specialized for any type of S, E because these types do not affect its behavior. View behaves the same for E=Int or E=YourCustomType, for example.

View is a pure domain component.

  • S - State
  • E - Event
pub struct View<'a, S: 'a, E: 'a> {
    pub evolve: EvolveFunction<'a, S, E>,
    pub initial_state: InitialStateFunction<'a, S>,
}

Materialized View

Materialized view is using/delegating a View to handle events of type E and to maintain a state of denormalized projection(s) as a result. Essentially, it represents the query/view side of the CQRS pattern. It belongs to the Application layer.

In order to handle the event, materialized view needs to fetch the current state via ViewStateRepository.fetchState suspending function first, and then delegate the event to the view, which can produce new state as a result. New state is then stored via ViewStateRepository.save suspending function.

Algebraic Data Types

In Rust, we can use ADTs to model our application's domain entities and relationships in a functional way, clearly defining the set of possible values and states. Rust has two main types of ADTs: enum and struct.

  • enum is used to define a type that can take on one of several possible variants - modeling a sum/OR type.
  • struct is used to express a type that has named fields - modeling a product/AND type.

ADTs will help with

  • representing the business domain in the code accurately
  • enforcing correctness
  • reducing the likelihood of bugs.

In FModel, we extensively use ADTs to model the data.

C / Command / Intent to change the state of the system

// models Sum/Or type / multiple possible variants
pub enum OrderCommand {
    Create(CreateOrderCommand),
    Update(UpdateOrderCommand),
    Cancel(CancelOrderCommand),
}
// models Product/And type / a concrete variant, consisting of named fields
pub struct CreateOrderCommand {
    pub order_id: u32,
    pub customer_name: String,
    pub items: Vec<String>,
}
// models Product/And type / a concrete variant, consisting of named fields
pub struct UpdateOrderCommand {
    pub order_id: u32,
    pub new_items: Vec<String>,
}
// models Product/And type / a concrete variant, consisting of named fields
#[derive(Debug)]
pub struct CancelOrderCommand {
    pub order_id: u32,
}

E / Event / Fact

// models Sum/Or type / multiple possible variants
pub enum OrderEvent {
    Created(OrderCreatedEvent),
    Updated(OrderUpdatedEvent),
    Cancelled(OrderCancelledEvent),
}
// models Product/And type / a concrete variant, consisting of named fields
pub struct OrderCreatedEvent {
    pub order_id: u32,
    pub customer_name: String,
    pub items: Vec<String>,
}
// models Product/And type / a concrete variant, consisting of named fields
pub struct OrderUpdatedEvent {
    pub order_id: u32,
    pub updated_items: Vec<String>,
}
// models Product/And type / a concrete variant, consisting of named fields
pub struct OrderCancelledEvent {
    pub order_id: u32,
}

S / State / Current state of the system/aggregate/entity

struct OrderState {
    order_id: u32,
    customer_name: String,
    items: Vec<String>,
    is_cancelled: bool,
}

Modeling the Behaviour of our domain

  • algebraic data types form the structure of our entities (commands, state, and events).
  • functions/lambda offers the algebra of manipulating the entities in a compositional manner, effectively modeling the behavior.

This leads to modularity in design and a clear separation of the entity’s structure and functions/behaviour of the entity.

Fmodel library offers generic and abstract components to specialize in for your specific case/expected behavior:

  • Decider - data type that represents the main decision-making algorithm.
fn decider<'a>() -> Decider<'a, OrderCommand, OrderState, OrderEvent> {
    Decider {
        // Your decision logic goes here.
        decide: Box::new(|command, state| match command {
            // Exhaustive pattern matching on the command
            OrderCommand::Create(create_cmd) => {
                vec![OrderEvent::Created(OrderCreatedEvent {
                    order_id: create_cmd.order_id,
                    customer_name: create_cmd.customer_name.to_owned(),
                    items: create_cmd.items.to_owned(),
                })]
            }
            OrderCommand::Update(update_cmd) => {
                // Your validation logic goes here
                if state.order_id == update_cmd.order_id {
                    vec![OrderEvent::Updated(OrderUpdatedEvent {
                        order_id: update_cmd.order_id,
                        updated_items: update_cmd.new_items.to_owned(),
                    })]
                } else {
                    // In case of validation failure, return empty list of events or error event
                    vec![]
                }
            }
            OrderCommand::Cancel(cancel_cmd) => {
                // Your validation logic goes here
                if state.order_id == cancel_cmd.order_id {
                    vec![OrderEvent::Cancelled(OrderCancelledEvent {
                        order_id: cancel_cmd.order_id,
                    })]
                } else {
                    // In case of validation failure, return empty list of events or error event
                    vec![]
                }
            }
        }),
        // Evolve the state based on the event(s)
        evolve: Box::new(|state, event| {
            let mut new_state = state.clone();
            // Exhaustive pattern matching on the event
            match event {
                OrderEvent::Created(created_event) => {
                    new_state.order_id = created_event.order_id;
                    new_state.customer_name = created_event.customer_name.to_owned();
                    new_state.items = created_event.items.to_owned();
                }
                OrderEvent::Updated(updated_event) => {
                    new_state.items = updated_event.updated_items.to_owned();
                }
                OrderEvent::Cancelled(_) => {
                    new_state.is_cancelled = true;
                }
            }
            new_state
        }),
        // Initial state
        initial_state: Box::new(|| OrderState {
            order_id: 0,
            customer_name: "".to_string(),
            items: Vec::new(),
            is_cancelled: false,
        }),
    }
}
  • View - represents the event handling algorithm responsible for translating the events into the denormalized state, which is adequate for querying.
// The state of the view component
struct OrderViewState {
    order_id: u32,
    customer_name: String,
    items: Vec<String>,
    is_cancelled: bool,
}

fn view<'a>() -> View<'a, OrderViewState, OrderEvent> {
    View {
        // Evolve the state of the `view` based on the event(s)
        evolve: Box::new(|state, event| {
            let mut new_state = state.clone();
            // Exhaustive pattern matching on the event
            match event {
                OrderEvent::Created(created_event) => {
                    new_state.order_id = created_event.order_id;
                    new_state.customer_name = created_event.customer_name.to_owned();
                    new_state.items = created_event.items.to_owned();
                }
                OrderEvent::Updated(updated_event) => {
                    new_state.items = updated_event.updated_items.to_owned();
                }
                OrderEvent::Cancelled(_) => {
                    new_state.is_cancelled = true;
                }
            }
            new_state
        }),
        // Initial state
        initial_state: Box::new(|| OrderViewState {
            order_id: 0,
            customer_name: "".to_string(),
            items: Vec::new(),
            is_cancelled: false,
        }),
    }
}

The Application layer

The logic execution will be orchestrated by the outside components that use the domain components (decider, view) to do the computations. These components will be responsible for fetching and saving the data (repositories).

The arrows in the image (adapters->application->domain) show the direction of the dependency. Notice that all dependencies point inward and that Domain does not depend on anybody or anything.

Pushing these decisions from the core domain model is very valuable. Being able to postpone them is a sign of good architecture.

Event-sourcing aggregate

    let repository = InMemoryOrderEventRepository::new();
    let aggregate = EventSourcedAggregate::new(repository, decider());

    let command = OrderCommand::Create(CreateOrderCommand {
        order_id: 1,
        customer_name: "John Doe".to_string(),
        items: vec!["Item 1".to_string(), "Item 2".to_string()],
    });

    let result = aggregate.handle(&command).await;
    assert!(result.is_ok());
    assert_eq!(
        result.unwrap(),
        [(
            OrderEvent::Created(OrderCreatedEvent {
                order_id: 1,
                customer_name: "John Doe".to_string(),
                items: vec!["Item 1".to_string(), "Item 2".to_string()],
            }),
            0
        )]
    );

State-stored aggregate

    let repository = InMemoryOrderStateRepository::new();
    let aggregate = StateStoredAggregate::new(repository, decider());

    let command = OrderCommand::Create(CreateOrderCommand {
        order_id: 1,
        customer_name: "John Doe".to_string(),
        items: vec!["Item 1".to_string(), "Item 2".to_string()],
    });
    let result = aggregate.handle(&command).await;
    assert!(result.is_ok());
    assert_eq!(
        result.unwrap(),
        (
            OrderState {
                order_id: 1,
                customer_name: "John Doe".to_string(),
                items: vec!["Item 1".to_string(), "Item 2".to_string()],
                is_cancelled: false,
            },
            0
        )
    );

Fearless Concurrency

Splitting the computation in your program into multiple threads to run multiple tasks at the same time can improve performance. However, programming with threads has a reputation for being difficult. Rust’s type system and ownership model guarantee thread safety.

Example of the concurrent execution of the aggregate:

async fn es_test() {
    let repository = InMemoryOrderEventRepository::new();
    let aggregate = Arc::new(EventSourcedAggregate::new(repository, decider()));
    // Makes a clone of the Arc pointer. This creates another pointer to the same allocation, increasing the strong reference count.
    let aggregate2 = Arc::clone(&aggregate);

    // Lets spawn two threads to simulate two concurrent requests
    let handle1 = thread::spawn(|| async move {
        let command = OrderCommand::Create(CreateOrderCommand {
            order_id: 1,
            customer_name: "John Doe".to_string(),
            items: vec!["Item 1".to_string(), "Item 2".to_string()],
        });

        let result = aggregate.handle(&command).await;
        assert!(result.is_ok());
        assert_eq!(
            result.unwrap(),
            [(
                OrderEvent::Created(OrderCreatedEvent {
                    order_id: 1,
                    customer_name: "John Doe".to_string(),
                    items: vec!["Item 1".to_string(), "Item 2".to_string()],
                }),
                0
            )]
        );
        let command = OrderCommand::Update(UpdateOrderCommand {
            order_id: 1,
            new_items: vec!["Item 3".to_string(), "Item 4".to_string()],
        });
        let result = aggregate.handle(&command).await;
        assert!(result.is_ok());
        assert_eq!(
            result.unwrap(),
            [(
                OrderEvent::Updated(OrderUpdatedEvent {
                    order_id: 1,
                    updated_items: vec!["Item 3".to_string(), "Item 4".to_string()],
                }),
                1
            )]
        );
        let command = OrderCommand::Cancel(CancelOrderCommand { order_id: 1 });
        let result = aggregate.handle(&command).await;
        assert!(result.is_ok());
        assert_eq!(
            result.unwrap(),
            [(
                OrderEvent::Cancelled(OrderCancelledEvent { order_id: 1 }),
                2
            )]
        );
    });

    let handle2 = thread::spawn(|| async move {
        let command = OrderCommand::Create(CreateOrderCommand {
            order_id: 2,
            customer_name: "John Doe".to_string(),
            items: vec!["Item 1".to_string(), "Item 2".to_string()],
        });
        let result = aggregate2.handle(&command).await;
        assert!(result.is_ok());
        assert_eq!(
            result.unwrap(),
            [(
                OrderEvent::Created(OrderCreatedEvent {
                    order_id: 2,
                    customer_name: "John Doe".to_string(),
                    items: vec!["Item 1".to_string(), "Item 2".to_string()],
                }),
                0
            )]
        );
        let command = OrderCommand::Update(UpdateOrderCommand {
            order_id: 2,
            new_items: vec!["Item 3".to_string(), "Item 4".to_string()],
        });
        let result = aggregate2.handle(&command).await;
        assert!(result.is_ok());
        assert_eq!(
            result.unwrap(),
            [(
                OrderEvent::Updated(OrderUpdatedEvent {
                    order_id: 2,
                    updated_items: vec!["Item 3".to_string(), "Item 4".to_string()],
                }),
                1
            )]
        );
        let command = OrderCommand::Cancel(CancelOrderCommand { order_id: 2 });
        let result = aggregate2.handle(&command).await;
        assert!(result.is_ok());
        assert_eq!(
            result.unwrap(),
            [(
                OrderEvent::Cancelled(OrderCancelledEvent { order_id: 2 }),
                2
            )]
        );
    });

    handle1.join().unwrap().await;
    handle2.join().unwrap().await;
}

You might wonder why all primitive types in Rust aren’t atomic and why standard library types aren’t implemented to use Arc<T> by default. The reason is that thread safety comes with a performance penalty that you only want to pay when you really need to.

You choose how to run it! You can run it in a single-threaded, multi-threaded, or distributed environment.

Install the crate as a dependency of your project

Run the following Cargo command in your project directory:

cargo add fmodel-rust

Or add the following line to your Cargo.toml file:

fmodel-rust = "0.2.0"

FModel in other languages

Further reading

Credits

Special credits to Jérémie Chassaing for sharing his research and Adam Dymitruk for hosting the meetup.


Created with ❤️ by Fraktalio

Comments
  • Update actions/checkout action to v4

    Update actions/checkout action to v4

    Mend Renovate

    This PR contains the following updates:

    | Package | Type | Update | Change | |---|---|---|---| | actions/checkout | action | major | v3 -> v4 |


    Release Notes

    actions/checkout (actions/checkout)

    v4

    Compare Source


    Configuration

    📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

    🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    🔕 Ignore: Close this PR and you won't be reminded about this update again.


    • [ ] If you want to rebase/retry this PR, check this box

    This PR has been generated by Mend Renovate. View repository job log here.

    opened by renovate[bot] 0
  • Update Rust crate tokio to 1.32.0

    Update Rust crate tokio to 1.32.0

    Mend Renovate

    This PR contains the following updates:

    | Package | Type | Update | Change | |---|---|---|---| | tokio (source) | dev-dependencies | minor | 1.0.0 -> 1.32.0 |


    Release Notes

    tokio-rs/tokio (tokio)

    v1.32.0: Tokio v1.32.0

    Compare Source

    Fixed
    • sync: fix potential quadradic behavior in broadcast::Receiver (#​5925)
    Added
    • process: stabilize Command::raw_arg (#​5930)
    • io: enable awaiting error readiness (#​5781)
    Unstable
    • rt(alt): improve the scalability of alt runtime as the number of cores grows (#​5935)

    v1.31.0: Tokio v1.31.0

    Compare Source

    Fixed
    • io: delegate WriteHalf::poll_write_vectored (#​5914)
    Unstable
    • rt(unstable): fix memory leak in unstable next-gen scheduler prototype (#​5911)
    • rt: expose mean task poll time metric (#​5927)

    v1.30.0: Tokio v1.30.0

    Compare Source

    1.30.0 (August 9, 2023)

    This release bumps the MSRV of Tokio to 1.63. (#​5887)

    Changed
    • tokio: reduce LLVM code generation (#​5859)
    • io: support --cfg mio_unsupported_force_poll_poll flag (#​5881)
    • sync: make const_new methods always available (#​5885)
    • sync: avoid false sharing in mpsc channel (#​5829)
    • rt: pop at least one task from inject queue (#​5908)
    Added
    • sync: add broadcast::Sender::new (#​5824)
    • net: implement UCred for espidf (#​5868)
    • fs: add File::options() (#​5869)
    • time: implement extra reset variants for Interval (#​5878)
    • process: add {ChildStd*}::into_owned_{fd, handle} (#​5899)
    Removed
    • tokio: removed unused tokio_* cfgs (#​5890)
    • remove build script to speed up compilation (#​5887)
    Documented
    • sync: mention lagging in docs for broadcast::send (#​5820)
    • runtime: expand on sharing runtime docs (#​5858)
    • io: use vec in example for AsyncReadExt::read_exact (#​5863)
    • time: mark Sleep as !Unpin in docs (#​5916)
    • process: fix raw_arg not showing up in docs (#​5865)
    Unstable
    • rt: add runtime ID (#​5864)
    • rt: initial implementation of new threaded runtime (#​5823)

    v1.29.1: Tokio v1.29.1

    Compare Source

    Fixed
    • rt: fix nesting two block_in_place with a block_on between (#​5837)

    v1.29.0: Tokio v1.29.0

    Compare Source

    Technically a breaking change, the Send implementation is removed from runtime::EnterGuard. This change fixes a bug and should not impact most users.

    Breaking
    • rt: EnterGuard should not be Send (#​5766)
    Fixed
    • fs: reduce blocking ops in fs::read_dir (#​5653)
    • rt: fix possible starvation (#​5686, #​5712)
    • rt: fix stacked borrows issue in JoinSet (#​5693)
    • rt: panic if EnterGuard dropped incorrect order (#​5772)
    • time: do not overflow to signal value (#​5710)
    • fs: wait for in-flight ops before cloning File (#​5803)
    Changed
    • rt: reduce time to poll tasks scheduled from outside the runtime (#​5705, #​5720)
    Added
    • net: add uds doc alias for unix sockets (#​5659)
    • rt: add metric for number of tasks (#​5628)
    • sync: implement more traits for channel errors (#​5666)
    • net: add nodelay methods on TcpSocket (#​5672)
    • sync: add broadcast::Receiver::blocking_recv (#​5690)
    • process: add raw_arg method to Command (#​5704)
    • io: support PRIORITY epoll events (#​5566)
    • task: add JoinSet::poll_join_next (#​5721)
    • net: add support for Redox OS (#​5790)
    Unstable

    v1.28.2: Tokio v1.28.2

    Compare Source

    1.28.2 (May 28, 2023)

    Forward ports 1.18.6 changes.

    Fixed
    • deps: disable default features for mio (#​5728)

    v1.28.1: Tokio v1.28.1

    Compare Source

    1.28.1 (May 10th, 2023)

    This release fixes a mistake in the build script that makes AsFd implementations unavailable on Rust 1.63. (#​5677)

    v1.28.0: Tokio v1.28.0

    Compare Source

    1.28.0 (April 25th, 2023)
    Added
    • io: add AsyncFd::async_io (#​5542)
    • io: impl BufMut for ReadBuf (#​5590)
    • net: add recv_buf for UdpSocket and UnixDatagram (#​5583)
    • sync: add OwnedSemaphorePermit::semaphore (#​5618)
    • sync: add same_channel to broadcast channel (#​5607)
    • sync: add watch::Receiver::wait_for (#​5611)
    • task: add JoinSet::spawn_blocking and JoinSet::spawn_blocking_on (#​5612)
    Changed
    • deps: update windows-sys to 0.48 (#​5591)
    • io: make read_to_end not grow unnecessarily (#​5610)
    • macros: make entrypoints more efficient (#​5621)
    • sync: improve Debug impl for RwLock (#​5647)
    • sync: reduce contention in Notify (#​5503)
    Fixed
    • net: support get_peer_cred on AIX (#​5065)
    • sync: avoid deadlocks in broadcast with custom wakers (#​5578)
    Documented
    • sync: fix typo in Semaphore::MAX_PERMITS (#​5645)
    • sync: fix typo in tokio::sync::watch::Sender docs (#​5587)

    v1.27.0: Tokio v1.27.0

    Compare Source

    1.27.0 (March 27th, 2023)

    This release bumps the MSRV of Tokio to 1.56. (#​5559)

    Added
    • io: add async_io helper method to sockets (#​5512)
    • io: add implementations of AsFd/AsHandle/AsSocket (#​5514, #​5540)
    • net: add UdpSocket::peek_sender() (#​5520)
    • sync: add RwLockWriteGuard::{downgrade_map, try_downgrade_map} (#​5527)
    • task: add JoinHandle::abort_handle (#​5543)
    Changed
    • io: use memchr from libc (#​5558)
    • macros: accept path as crate rename in #[tokio::main] (#​5557)
    • macros: update to syn 2.0.0 (#​5572)
    • time: don't register for a wakeup when Interval returns Ready (#​5553)
    Fixed
    • fs: fuse std iterator in ReadDir (#​5555)
    • tracing: fix spawn_blocking location fields (#​5573)
    • time: clean up redundant check in Wheel::poll() (#​5574)
    Documented
    • macros: define cancellation safety (#​5525)
    • io: add details to docs of tokio::io::copy[_buf] (#​5575)
    • io: refer to ReaderStream and StreamReader in module docs (#​5576)

    v1.26.0: Tokio v1.26.0

    Compare Source

    Fixed
    Added
    Changed
    Internal Changes
    Unstable
    Documented

    v1.25.1: Tokio v1.25.1

    Compare Source

    1.25.1 (May 28, 2023)

    Forward ports 1.18.6 changes.

    Fixed
    • deps: disable default features for mio (#​5728)

    v1.25.0: Tokio v1.25.0

    1.25.0 (January 28, 2023)

    Fixed
    • rt: fix runtime metrics reporting (#​5330)
    Added
    • sync: add broadcast::Sender::len (#​5343)
    Changed
    • fs: increase maximum read buffer size to 2MiB (#​5397)

    v1.24.1: Tokio v1.24.1

    Compare Source

    This release fixes a compilation failure on targets without AtomicU64 when using rustc older than 1.63. (#​5356)

    v1.24.0: Tokio v1.24.0

    Compare Source

    The highlight of this release is the reduction of lock contention for all I/O operations (#​5300). We have received reports of up to a 20% improvement in CPU utilization and increased throughput for real-world I/O heavy applications.

    Fixed
    • rt: improve native AtomicU64 support detection (#​5284)
    Added
    • rt: add configuration option for max number of I/O events polled from the OS per tick (#​5186)
    • rt: add an environment variable for configuring the default number of worker threads per runtime instance (#​4250)
    Changed
    • sync: reduce MPSC channel stack usage (#​5294)
    • io: reduce lock contention in I/O operations (#​5300)
    • fs: speed up read_dir() by chunking operations (#​5309)
    • rt: use internal ThreadId implementation (#​5329)
    • test: don't auto-advance time when a spawn_blocking task is running (#​5115)

    v1.23.1: Tokio v1.23.1

    Compare Source

    This release forward ports changes from 1.18.4.

    Fixed
    • net: fix Windows named pipe server builder to maintain option when toggling pipe mode (#​5336).

    v1.23.0: Tokio v1.23.0

    Compare Source

    Fixed
    • net: fix Windows named pipe connect (#​5208)
    • io: support vectored writes for ChildStdin (#​5216)
    • io: fix async fn ready() false positive for OS-specific events (#​5231)
    Changed
    • runtime: yield_now defers task until after driver poll (#​5223)
    • runtime: reduce amount of codegen needed per spawned task (#​5213)
    • windows: replace winapi dependency with windows-sys (#​5204)

    v1.22.0: Tokio v1.22.0

    Compare Source

    Added
    • runtime: add Handle::runtime_flavor (#​5138)
    • sync: add Mutex::blocking_lock_owned (#​5130)
    • sync: add Semaphore::MAX_PERMITS (#​5144)
    • sync: add merge() to semaphore permits (#​4948)
    • sync: add mpsc::WeakUnboundedSender (#​5189)
    Added (unstable)
    • process: add Command::process_group (#​5114)
    • runtime: export metrics about the blocking thread pool (#​5161)
    • task: add task::id() and task::try_id() (#​5171)
    Fixed
    • macros: don't take ownership of futures in macros (#​5087)
    • runtime: fix Stacked Borrows violation in LocalOwnedTasks (#​5099)
    • runtime: mitigate ABA with 32-bit queue indices when possible (#​5042)
    • task: wake local tasks to the local queue when woken by the same thread (#​5095)
    • time: panic in release mode when mark_pending called illegally (#​5093)
    • runtime: fix typo in expect message (#​5169)
    • runtime: fix unsync_load on atomic types (#​5175)
    • task: elaborate safety comments in task deallocation (#​5172)
    • runtime: fix LocalSet drop in thread local (#​5179)
    • net: remove libc type leakage in a public API (#​5191)
    • runtime: update the alignment of CachePadded (#​5106)
    Changed
    • io: make tokio::io::copy continue filling the buffer when writer stalls (#​5066)
    • runtime: remove coop::budget from LocalSet::run_until (#​5155)
    • sync: make Notify panic safe (#​5154)
    Documented
    • io: fix doc for write_i8 to use signed integers (#​5040)
    • net: fix doc typos for TCP and UDP set_tos methods (#​5073)
    • net: fix function name in UdpSocket::recv documentation (#​5150)
    • sync: typo in TryLockError for RwLock::try_write (#​5160)
    • task: document that spawned tasks execute immediately (#​5117)
    • time: document return type of timeout (#​5118)
    • time: document that timeout checks only before poll (#​5126)
    • sync: specify return type of oneshot::Receiver in docs (#​5198)
    Internal changes
    • runtime: use const Mutex::new for globals (#​5061)
    • runtime: remove Option around mio::Events in io driver (#​5078)
    • runtime: remove a conditional compilation clause (#​5104)
    • runtime: remove a reference to internal time handle (#​5107)
    • runtime: misc time driver cleanup (#​5120)
    • runtime: move signal driver to runtime module (#​5121)
    • runtime: signal driver now uses I/O driver directly (#​5125)
    • runtime: start decoupling I/O driver and I/O handle (#​5127)
    • runtime: switch io::handle refs with scheduler:Handle (#​5128)
    • runtime: remove Arc from I/O driver (#​5134)
    • runtime: use signal driver handle via scheduler::Handle (#​5135)
    • runtime: move internal clock fns out of context (#​5139)
    • runtime: remove runtime::context module (#​5140)
    • runtime: keep driver cfgs in driver.rs (#​5141)
    • runtime: add runtime::context to unify thread-locals (#​5143)
    • runtime: rename some confusing internal variables/fns (#​5151)
    • runtime: move coop mod into runtime (#​5152)
    • runtime: move budget state to context thread-local (#​5157)
    • runtime: move park logic into runtime module (#​5158)
    • runtime: move Runtime into its own file (#​5159)
    • runtime: unify entering a runtime with Handle::enter (#​5163)
    • runtime: remove handle reference from each scheduler (#​5166)
    • runtime: move enter into context (#​5167)
    • runtime: combine context and entered thread-locals (#​5168)
    • runtime: fix accidental unsetting of current handle (#​5178)
    • runtime: move CoreStage methods to Core (#​5182)
    • sync: name mpsc semaphore types (#​5146)

    v1.21.2: Tokio v1.21.2

    Compare Source

    1.21.2 (September 27, 2022)

    This release removes the dependency on the once_cell crate to restore the MSRV of 1.21.x, which is the latest minor version at the time of release. (#​5048)

    v1.21.1: Tokio v1.21.1

    Compare Source

    1.21.1 (September 13, 2022)

    Fixed
    • net: fix dependency resolution for socket2 (#​5000)
    • task: ignore failure to set TLS in LocalSet Drop (#​4976)

    v1.21.0: Tokio v1.21.0

    Compare Source

    1.21.0 (September 2, 2022)

    This release is the first release of Tokio to intentionally support WASM. The sync,macros,io-util,rt,time features are stabilized on WASM. Additionally the wasm32-wasi target is given unstable support for the net feature.

    Added
    • net: add device and bind_device methods to TCP/UDP sockets (#​4882)
    • net: add tos and set_tos methods to TCP and UDP sockets (#​4877)
    • net: add security flags to named pipe ServerOptions (#​4845)
    • signal: add more windows signal handlers (#​4924)
    • sync: add mpsc::Sender::max_capacity method (#​4904)
    • sync: implement Weak version of mpsc::Sender (#​4595)
    • task: add LocalSet::enter (#​4765)
    • task: stabilize JoinSet and AbortHandle (#​4920)
    • tokio: add track_caller to public APIs (#​4805, #​4848, #​4852)
    • wasm: initial support for wasm32-wasi target (#​4716)
    Fixed
    • miri: improve miri compatibility by avoiding temporary references in linked_list::Link impls (#​4841)
    • signal: don't register write interest on signal pipe (#​4898)
    • sync: add #[must_use] to lock guards (#​4886)
    • sync: fix hang when calling recv on closed and reopened broadcast channel (#​4867)
    • task: propagate attributes on task-locals (#​4837)
    Changed
    • fs: change panic to error in File::start_seek (#​4897)
    • io: reduce syscalls in poll_read (#​4840)
    • process: use blocking threadpool for child stdio I/O (#​4824)
    • signal: make SignalKind methods const (#​4956)
    Internal changes
    • rt: extract basic_scheduler::Config (#​4935)
    • rt: move I/O driver into runtime module (#​4942)
    • rt: rename internal scheduler types (#​4945)
    Documented
    • chore: fix typos and grammar (#​4858, #​4894, #​4928)
    • io: fix typo in AsyncSeekExt::rewind docs (#​4893)
    • net: add documentation to try_read() for zero-length buffers (#​4937)
    • runtime: remove incorrect panic section for Builder::worker_threads (#​4849)
    • sync: doc of watch::Sender::send improved (#​4959)
    • task: add cancel safety docs to JoinHandle (#​4901)
    • task: expand on cancellation of spawn_blocking (#​4811)
    • time: clarify that the first tick of Interval::tick happens immediately (#​4951)
    Unstable
    • rt: add unstable option to disable the LIFO slot (#​4936)
    • task: fix incorrect signature in Builder::spawn_on (#​4953)
    • task: make task::Builder::spawn* methods fallible (#​4823)

    v1.20.5: Tokio v1.20.5

    Compare Source

    1.20.5 (May 28, 2023)

    Forward ports 1.18.6 changes.

    Fixed
    • deps: disable default features for mio (#​5728)

    v1.20.4

    Compare Source

    v1.20.3

    Compare Source

    v1.20.2: Tokio v1.20.2

    Compare Source

    1.20.2 (September 27, 2022)

    This release removes the dependency on the once_cell crate to restore the MSRV of the 1.20.x LTS release. (#​5048)

    v1.20.1: Tokio v1.20.1

    Compare Source

    1.20.1 (July 25, 2022)

    Fixed
    • chore: fix version detection in build script (#​4860)

    v1.20.0: Tokio v1.20.0

    Compare Source

    1.20.0 (July 12, 2022)

    Added
    Changed
    • time: remove src/time/driver/wheel/stack.rs (#​4766)
    • rt: clean up arguments passed to basic scheduler (#​4767)
    • net: be more specific about winapi features (#​4764)
    • tokio: use const initialized thread locals where possible (#​4677)
    • task: various small improvements to LocalKey (#​4795)
    Fixed
    Documented
    • fs: warn about performance pitfall (#​4762)
    • chore: fix spelling (#​4769)
    • sync: document spurious failures in oneshot (#​4777)
    • sync: add warning for watch in non-Send futures (#​4741)
    • chore: fix typo (#​4798)
    Unstable
    • joinset: rename join_one to join_next (#​4755)
    • rt: unhandled panic config for current thread rt (#​4770)

    v1.19.2: Tokio v1.19.2

    Compare Source

    1.19.2 (June 6, 2022)

    This release fixes another bug in Notified::enable. (#​4751)

    v1.19.1: Tokio v1.19.1

    Compare Source

    1.19.1 (June 5, 2022)

    This release fixes a bug in Notified::enable. (#​4747)

    v1.19.0: Tokio v1.19.0

    Compare Source

    1.19.0 (June 3, 2022)

    Added
    • runtime: add is_finished method for JoinHandle and AbortHandle (#​4709)
    • runtime: make global queue and event polling intervals configurable (#​4671)
    • sync: add Notified::enable (#​4705)
    • sync: add watch::Sender::send_if_modified (#​4591)
    • sync: add resubscribe method to broadcast::Receiver (#​4607)
    • net: add take_error to TcpSocket and TcpStream (#​4739)
    Changed
    • io: refactor out usage of Weak in the io handle (#​4656)
    Fixed
    • macros: avoid starvation in join! and try_join! (#​4624)
    Documented
    • runtime: clarify semantics of tasks outliving block_on (#​4729)
    • time: fix example for MissedTickBehavior::Burst (#​4713)
    Unstable
    • metrics: correctly update atomics in IoDriverMetrics (#​4725)
    • metrics: fix compilation with unstable, process, and rt, but without net (#​4682)
    • task: add #[track_caller] to JoinSet/JoinMap (#​4697)
    • task: add Builder::{spawn_on, spawn_local_on, spawn_blocking_on} (#​4683)
    • task: add consume_budget for cooperative scheduling (#​4498)
    • task: add join_set::Builder for configuring JoinSet tasks (#​4687)
    • task: update return value of JoinSet::join_one (#​4726)

    v1.18.6: Tokio v1.18.6

    Compare Source

    1.18.6 (May 28, 2023)

    Fixed
    • deps: disable default features for mio (#​5728)

    v1.18.5

    Compare Source

    v1.18.4

    Compare Source

    v1.18.3: Tokio v1.18.3

    Compare Source

    1.18.3 (September 27, 2022)

    This release removes the dependency on the once_cell crate to restore the MSRV of the 1.18.x LTS release. (#​5048)

    v1.18.2: Tokio v1.18.2

    Compare Source

    1.18.2 (May 5, 2022)

    Add missing features for the winapi dependency. (#​4663)

    v1.18.1: Tokio v1.18.1

    Compare Source

    1.18.1 (May 2, 2022)

    The 1.18.0 release broke the build for targets without 64-bit atomics when building with tokio_unstable. This release fixes that. (#​4649)

    v1.18.0: Tokio v1.18.0

    Compare Source

    1.18.0 (April 27, 2022)

    This release adds a number of new APIs in tokio::net, tokio::signal, and tokio::sync. In addition, it adds new unstable APIs to tokio::task (Ids for uniquely identifying a task, and AbortHandle for remotely cancelling a task), as well as a number of bugfixes.

    Fixed
    • blocking: add missing #[track_caller] for spawn_blocking (#​4616)
    • macros: fix select macro to process 64 branches (#​4519)
    • net: fix try_io methods not calling Mio's try_io internally (#​4582)
    • runtime: recover when OS fails to spawn a new thread (#​4485)
    Added
    • net: add UdpSocket::peer_addr (#​4611)
    • net: add try_read_buf method for named pipes (#​4626)
    • signal: add SignalKind Hash/Eq impls and c_int conversion (#​4540)
    • signal: add support for signals up to SIGRTMAX (#​4555)
    • sync: add watch::Sender::send_modify method (#​4310)
    • sync: add broadcast::Receiver::len method (#​4542)
    • sync: add watch::Receiver::same_channel method (#​4581)
    • sync: implement Clone for RecvError types (#​4560)
    Changed
    • update mio to 0.8.1 (#​4582)
    • macros: rename tokio::select!'s internal util module (#​4543)
    • runtime: use Vec::with_capacity when building runtime (#​4553)
    Documented
    • improve docs for tokio_unstable (#​4524)
    • runtime: include more documentation for thread_pool/worker (#​4511)
    • runtime: update Handle::current's docs to mention EnterGuard (#​4567)
    • time: clarify platform specific timer resolution (#​4474)
    • signal: document that Signal::recv is cancel-safe (#​4634)
    • sync: UnboundedReceiver close docs (#​4548)
    Unstable

    The following changes only apply when building with --cfg tokio_unstable:

    • task: add task::Id type (#​4630)
    • task: add AbortHandle type for cancelling tasks in a JoinSet (#​4530], [#​4640)
    • task: fix missing doc(cfg(...)) attributes for JoinSet (#​4531)
    • task: fix broken link in AbortHandle RustDoc (#​4545)
    • metrics: add initial IO driver metrics (#​4507)

    v1.17.0: Tokio v1.17.0

    Compare Source

    1.17.0 (February 15, 2022)

    This release updates the minimum supported Rust version (MSRV) to 1.49, the mio dependency to v0.8, and the (optional) parking_lot dependency to v0.12. Additionally, it contains several bug fixes, as well as internal refactoring and performance improvements.

    Fixed
    • time: prevent panicking in sleep with large durations (#​4495)
    • time: eliminate potential panics in Instant arithmetic on platforms where Instant::now is not monotonic (#​4461)
    • io: fix DuplexStream not participating in cooperative yielding (#​4478)
    • rt: fix potential double panic when dropping a JoinHandle (#​4430)
    Changed
    • update minimum supported Rust version to 1.49 (#​4457)
    • update parking_lot dependency to v0.12.0 (#​4459)
    • update mio dependency to v0.8 (#​4449)
    • rt: remove an unnecessary lock in the blocking pool (#​4436)
    • rt: remove an unnecessary enum in the basic scheduler (#​4462)
    • time: use bit manipulation instead of modulo to improve performance (#​4480)
    • net: use std::future::Ready instead of our own Ready future (#​4271)
    • replace deprecated atomic::spin_loop_hint with hint::spin_loop (#​4491)
    • fix miri failures in intrusive linked lists (#​4397)
    Documented
    • io: add an example for tokio::process::ChildStdin (#​4479)
    Unstable

    The following changes only apply when building with --cfg tokio_unstable:

    • task: fix missing location information in tracing spans generated by spawn_local (#​4483)
    • task: add JoinSet for managing sets of tasks (#​4335)
    • metrics: fix compilation error on MIPS (#​4475)
    • metrics: fix compilation error on arm32v7 (#​4453)

    v1.16.1: Tokio v1.16.1

    Compare Source

    1.16.1 (January 28, 2022)

    This release fixes a bug in #​4428 with the change #​4437.

    v1.16.0: Tokio v1.16.0

    Compare Source

    Fixes a soundness bug in io::Take (#​4428). The unsoundness is exposed when leaking memory in the given AsyncRead implementation and then overwriting the supplied buffer:

    impl AsyncRead for Buggy {
        fn poll_read(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            buf: &mut ReadBuf<'_>
        ) -> Poll<Result<()>> {
          let new_buf = vec![0; 5].leak();
          *buf = ReadBuf::new(new_buf);
          buf.put_slice(b"hello");
          Poll::Ready(Ok(()))
        }
    }
    

    Also, this release includes improvements to the multi-threaded scheduler that can increase throughput by up to 20% in some cases (#​4383).

    Fixed
    • io: soundness don't expose uninitialized memory when using io::Take in edge case (#​4428)
    • fs: ensure File::write results in a write syscall when the runtime shuts down (#​4316)
    • process: drop pipe after child exits in wait_with_output (#​4315)
    • rt: improve error message when spawning a thread fails (#​4398)
    • rt: reduce false-positive thread wakups in the multi-threaded scheduler (#​4383)
    • sync: don't inherit Send from parking_lot::*Guard (#​4359)
    Added
    • net: TcpSocket::linger() and set_linger() (#​4324)
    • net: impl UnwindSafe for socket types (#​4384)
    • rt: impl UnwindSafe for JoinHandle (#​4418)
    • sync: watch::Receiver::has_changed() (#​4342)
    • sync: oneshot::Receiver::blocking_recv() (#​4334)
    • sync: RwLock blocking operations (#​4425)
    Unstable

    The following changes only apply when building with --cfg tokio_unstable

    • rt: breaking change overhaul runtime metrics API ([#​4373])

    Configuration

    📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

    🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    🔕 Ignore: Close this PR and you won't be reminded about this update again.


    • [ ] If you want to rebase/retry this PR, check this box

    This PR has been generated by Mend Renovate. View repository job log here.

    opened by renovate[bot] 0
  • Update Rust crate async-trait to 0.1.73

    Update Rust crate async-trait to 0.1.73

    Mend Renovate

    This PR contains the following updates:

    | Package | Type | Update | Change | |---|---|---|---| | async-trait | dependencies | patch | 0.1.49 -> 0.1.73 |


    Release Notes

    dtolnay/async-trait (async-trait)

    v0.1.73

    Compare Source

    • Prevent generated code from triggering ignored_unit_patterns pedantic clippy lint

    v0.1.72

    Compare Source

    • Documentation improvements

    v0.1.71

    Compare Source

    • Documentation improvements

    v0.1.70

    Compare Source

    v0.1.69

    Compare Source

    • Resolve new diverging_sub_expression clippy lint in generated code

    v0.1.68

    Compare Source

    • Improve error message if an async fn is written without a function body in an impl block

    v0.1.67

    Compare Source

    • Update syn dependency to 2.x

    v0.1.66

    Compare Source

    • Set html_root_url attribute

    v0.1.65

    Compare Source

    • Fix interaction with rustc's single_use_lifetimes lint (#​238, #​239)

    v0.1.64

    Compare Source

    • Suppress async_yields_async clippy correctness lint in generated code (#​236, #​237)

    v0.1.63

    Compare Source

    • Do not require Sync on unused shared reference arguments (#​232, #​233)
    • Make expansion of nested _ and .. patterns edition independent (#​234, #​235)

    v0.1.62

    Compare Source

    • Improve error message involving elided lifetimes (#​229)

    v0.1.61

    Compare Source

    • Fix async function signatures that involve #[cfg(...)] attributes on parameters (#​227, thanks @​azriel91)

    v0.1.60

    Compare Source

    • Documentation improvements

    v0.1.59

    Compare Source

    • Support self: Arc<Self> async methods that have a default implementation provided by the trait (#​210)

    v0.1.58

    Compare Source

    • Improve rust-analyzer "go to definition" on the method names of an async trait (#​218)

    v0.1.57

    Compare Source

    • Add categories to crates.io metadata

    v0.1.56

    Compare Source

    • Fix build errors that involve use parentheses to disambiguate: `(impl 'async_trait + Trait)` (#​204)

    v0.1.55

    Compare Source

    • Fix drop order of uncaptured arguments (_) when compiling in 2021 edition's closure capture rules (#​199)

    v0.1.54

    Compare Source

    • Fix lifetime issue when using impl Trait in an async function argument type (#​177)

    v0.1.53

    Compare Source

    • Improve diagnostic / suggested fixes which involve "consider further restricting this bound" (#​194)

    v0.1.52

    Compare Source

    • Eliminate clippy::shadow_some restriction lint from generated code (#​184, thanks @​c410-f3r)

    v0.1.51

    Compare Source

    v0.1.50

    Compare Source

    • Make compatible with tokio::select, futures::select and similar macros (#​161)

    Configuration

    📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

    🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    🔕 Ignore: Close this PR and you won't be reminded about this update again.


    • [ ] If you want to rebase/retry this PR, check this box

    This PR has been generated by Mend Renovate. View repository job log here.

    opened by renovate[bot] 0
  • Configure Renovate

    Configure Renovate

    Mend Renovate

    Welcome to Renovate! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin.

    🚦 To activate Renovate, merge this Pull Request. To disable Renovate, simply close this Pull Request unmerged.


    Detected Package Files

    • Cargo.toml (cargo)
    • .github/workflows/build.yml (github-actions)
    • .github/workflows/publish.yml (github-actions)

    Configuration Summary

    Based on the default config's presets, Renovate will:

    • Start dependency updates only once this onboarding PR is merged
    • Show all Merge Confidence badges for pull requests.
    • Enable Renovate Dependency Dashboard creation.
    • Use semantic commit type fix for dependencies and chore for all others if semantic commits are in use.
    • Ignore node_modules, bower_components, vendor and various test/tests directories.
    • Group known monorepo packages together.
    • Use curated list of recommended non-monorepo package groupings.
    • Apply crowd-sourced package replacement rules.
    • Apply crowd-sourced workarounds for known problems with packages.

    🔡 Would you like to change the way Renovate is upgrading your dependencies? Simply edit the renovate.json in this branch with your custom config and the list of Pull Requests in the "What to Expect" section below will be updated the next time Renovate runs.


    What to Expect

    With your current configuration, Renovate will create 3 Pull Requests:

    Update Rust crate async-trait to 0.1.73
    • Schedule: ["at any time"]
    • Branch name: renovate/async-trait-0.x
    • Merge into: main
    • Upgrade async-trait to 0.1.73
    Update Rust crate tokio to 1.32.0
    • Schedule: ["at any time"]
    • Branch name: renovate/tokio-1.x
    • Merge into: main
    • Upgrade tokio to 1.32.0
    Update actions/checkout action to v4
    • Schedule: ["at any time"]
    • Branch name: renovate/actions-checkout-4.x
    • Merge into: main
    • Upgrade actions/checkout to v4

    🚸 Branch creation will be limited to maximum 2 per hour, so it doesn't swamp any CI resources or overwhelm the project. See docs for prhourlylimit for details.


    ❓ Got questions? Check out Renovate's Docs, particularly the Getting Started section. If you need any further assistance then you can also request help here.


    This PR has been generated by Mend Renovate. View repository job log here.

    opened by renovate[bot] 0
  • DecideFunction - why Vec<E>

    DecideFunction - why Vec

    Thanks so much for pushing this work forward as a community project ❤️ 👏🏻 🎉

    As a practitioner of Event Sourcing based on Greg Young's training materials across a couple companies and three languages now, I have some in-the-weeds questions if you are open to discussing them here? If not, that's fine, and thanks!

    1. Why does DecideFunction return Vec<E>?

    Yes, it totally makes sense to return Events rather than imperatively store them

    In a traditional CommandHandler as taught by Greg Young we often see in C# an interface like this:

    public interface ICommandHandler<TCommand> where TCommand : Message {
        void Handle(TCommand message);
    }
    

    👍🏻 I'm 100% understanding (and agreeing!) that a design goal of Fmodel appears to be bringing some power of functional programming to these patterns, so of course we want to return the Events.

    By contrast the traditional Greg Young-provided code patterns as above are deeply imperative, and as we see in official examples, the void Handle(TCommand message) implies the following dependency management chain being called as side effects:

    1. ICommandHandler#Handle has an IRepository (see DDD Repository pattern)
    2. IRepository#Save(AggregateRoot aggregate, int expectedVersion) if you are doing Event Sourcing for this aggregate has an IEventStore
    3. IEventStore#SaveEvents(Guid aggregateId, IEnumerable<Event> events, int expectedVersion) has a concrete implementation of recording the events to the stream of aggregateId or returning an Optimistic Concurrency error if the expectedVersion is behind the last event saved in the stream

    So it makes sense to return the Event(s), but why a Vec?

    Yes, it totally makes sense that sometimes a Command generates 2+ domain Events

    Sometimes the best way to model the domain is for one Command to result in multiple Events (e.g. an Axon framework example, an Elixer example, a C# example).

    Although I vaguely recall that in one of the training videos, Greg Young suggests that we should always question instances of 1:N Command:Events pattern when it comes up -- I vaguely recall the suggestion that 1:1 pairing between Command and Event are generally best at modeling that "a user intent succeeded and it is now a fact", and so this should be the common case?

    So it make sense there could be 0, 1, or more Events, but why a Vec?

    Do we really need to allocate an owning Vec on every single Command that succeeds?

    My understanding is that by making the return type of DecideFunction Vec<E>, we are saying a side-effect of every single decision about a Command in the system will be a heap allocation. Is this the right approach? I'm assuming the following:

    1. Most decisions on a Command should return either a rejection (more on this) or 1 Event
    2. A few could result in 2-3 Events, and returning more than 3 would be quite rare
    3. The resulting events in memory are likely to be short-lived and handled quickly - recorded to a durable event stream and perhaps applied to a model or published to a notification system

    If those assumptions are correct, I was wondering if the return type should be more like:

    -> Result<CommandFailedError, impl Iterator<Item=E> + '_ >
    

    In other words, an explicitly modeled rejection of the command, or an interface that allows a zero cost implementation such as a slice of 1-2 Events on the stack?

    Q1: Would such an approach allow returning a slice containing a single Event on the stack, avoiding allocation but preserving that the return is iterable?

    Q2: Why does Fmodel not model Rejection of Commands in a first class way, such via a Result type?

    Thank you!

    opened by ms-ati 2
  • Dependency Dashboard

    Dependency Dashboard

    This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

    This repository currently has no open or pending branches.

    Detected dependencies

    cargo
    Cargo.toml
    • async-trait 0.1.73
    • derive_more 0.99.17
    • tokio 1.32.0
    github-actions
    .github/workflows/build.yml
    • actions/checkout v4
    .github/workflows/publish.yml
    • actions/checkout v4

    • [ ] Check this box to trigger a request for Renovate to run again on this repository
    opened by renovate[bot] 0
Releases(0.2.0)
  • 0.2.0(Sep 21, 2023)

    fmodel-rust library is optimized for Event sourcing, CQRS, and Domain Modeling.

    The goal is to accelerate the development of compositional, safe, and ergonomic applications. Fmodel uses a powerful Rust type-system to bring functional and algebraic domain modeling to Rust.

    We appreciate your feedback. Discuss it. Tweet it.

    • EventSourcedAggregate abstracted to use Traits on the fields, not concrete implementation of the Decider
    • MaterializedView abstracted to use Traits on the fields, not concrete implementation of the Decider
    • Enabling concurrent usage of the aggregate/materialized view in multi-threaded runtimes.

    What's Changed

    • Configure Renovate by @renovate in https://github.com/fraktalio/fmodel-rust/pull/2
    • Update Rust crate async-trait to 0.1.73 by @renovate in https://github.com/fraktalio/fmodel-rust/pull/3
    • Update Rust crate tokio to 1.32.0 by @renovate in https://github.com/fraktalio/fmodel-rust/pull/4
    • Update actions/checkout action to v4 by @renovate in https://github.com/fraktalio/fmodel-rust/pull/6

    New Contributors

    • @renovate made their first contribution in https://github.com/fraktalio/fmodel-rust/pull/2

    Full Changelog: https://github.com/fraktalio/fmodel-rust/compare/0.1.0...0.2.0


    Created with :heart: by Fraktalio

    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Sep 16, 2023)

    fmodel-rust library is optimized for Event sourcing, CQRS, and Domain Modeling.

    This is the first release of the library: 0.1.0. It is published on crates.io.

    The goal is to accelerate the development of compositional, safe, and ergonomic applications. Fmodel aims to bring functional and algebraic domain modeling to Rust.

    We appreciate your feedback. Discuss it. Tweet it.


    Created with :heart: by Fraktalio

    Source code(tar.gz)
    Source code(zip)
Owner
Fraktalio
Open Source projects supported by Fraktalio - Software design and development
Fraktalio
Now, the Host is Mine! - Super Fast Sub-domain Takeover Detection!

NtH1M - Super Fast Sub-domain Takeover Detection Notice This is a sad notice that our Internet Hero (@TheBinitGhimire) had recent demise on 26th of Ju

Captain Nick Lucifer* 5 Nov 5, 2022
A Domain Driven Design example application in Rust.

Rust Domain Driven Design Example rust-ddd Rust Domain-Driven-Design (DDD) Summery This repository is used to present how I find implementing DDD in R

Behrouz R.Farsi 6 Nov 15, 2022
A domain-specific language for Infrastructure as Code

Skyr A domain-specific language for Infrastructure As Code, with a phased execution model, allowing for expressive and highly dynamic IaC solutions. D

Emil Broman 4 Mar 11, 2023
Proof-of-concept for a memory-efficient data structure for zooming billion-event traces

Proof-of-concept for a gigabyte-scale trace viewer This repo includes: A memory-efficient representation for event traces An unusually simple and memo

Tristan Hume 59 Sep 5, 2022
Rust library to facilitate event-driven programming.

Squeak Squeak is a zero-dependency Rust library to facilitate event-driven programming. Examples use squeak::{Delegate, Response}; let on_damage_rece

Antoine Gersant 58 Dec 31, 2022
Harvest Moon: (More) Friends of Mineral Town event script compiler

mary This is a script compiler for Harvest Moon: Friends of Mineral Town and Harvest Moon: More Friends of Mineral Town for the GBA. The end goal is f

Nat (Stan) 5 Oct 23, 2023
Simple Event-Driven Microservice Architecture in Rust

Simple Event-Driven Microservice Architecture in Rust is an open-source project showcasing the simplicity and efficiency of building microservices in Rust. This minimalistic project demonstrates an e-commerce backend system, featuring just two microservices: Catalog and Order.

James Mallon 4 Dec 19, 2023
A lightweight, opinionated CQRS and event sourcing framework targeting serverless architectures.

cqrs A lightweight, opinionated CQRS and event sourcing framework targeting serverless architectures. Command Query Responsibility Segregation (CQRS)

Serverless Technology 161 Dec 29, 2022
Thalo is an event-sourcing framework for building large scale systems

Thalo Event sourcing framework for building microservices. Overview Thalo is an event-sourcing framework for building large scale systems based on the

null 548 Jan 3, 2023
Event-sourcing Schema Definition Language

ESDL Event-sourcing Schema Definition Language Schema definition language for defining aggregates, commands, events & custom types. Heavily inspired b

null 35 Dec 15, 2022
Rust library that helps you change the domain of the link to another domain 🦀🔐

Rust library that helps you to change the domain of the link to another domain, the library helps with privacy. It can be used to change the domain of sites that do not care about privacy to another that does.

TheAwiteb 2 Mar 28, 2022
A toy event store and event messaging system.

RDeeBee Follow this blog series for more details on this project. This system is inspired by Martin Kleppman's arguments that Event Sourcing system an

null 4 Nov 6, 2022
Open sourcing a profitable MEV Arbitrage Bot written in blazing fast Rust.

Dex Arbitrage - MEV Bot Open sourcing a profitable MEV Arbitrage Bot written in blazing fast Rust. Before Starting I am a self-taught programmer, a co

null 4 Sep 18, 2023
Linear Programming for Rust, with an user-friendly API. This crate allows modeling LP problems, and let's you solve them with various solvers.

good_lp A Linear Programming modeler that is easy to use, performant with large problems, and well-typed. use good_lp::{variables, variable, coin_cbc,

Rust Operations Research 101 Dec 27, 2022
unFlow is a Design as Code implementation, a DSL for UX & backend modeling. DSL to Sketch file, Sketch to DSL, DSL to code.

unflow 是一个低代码、无代码设计语言。unFlow is a Design as Code implementation, a DSL for UX & backend modeling. DSL to Sketch file, Sketch to DSL, DSL to code.

Inherd OS Team (硬核开源小组) 70 Nov 27, 2022
Modeling is a tools to analysis different languages by Ctags

Modeling Modeling is a tools to analysis different languages by Ctags process: generate to opt call ctags with opt analysis ctags logs output resulse

Inherd OS Team (硬核开源小组) 13 Sep 13, 2022
Data structures and algorithms for 3D geometric modeling.

geom3d Data structures and algorithms for 3D geometric modeling. Features: Bezier curve and surface B-Spline curve and surface Spin surface Sweep surf

Junfeng Liu 31 Sep 20, 2022
A 3D modeling and rendering programming language utilizing SDFs.

ForgedThoughts is a modeling and rendering programming language utilizing SDFs and is in early development. For documentation and examples see the Web

Markus Moenig 4 Feb 27, 2023
RedMaple offers an oppinionated yet extremely flexible data modeling system based on events for back-end applications.

RedMaple offers an oppinionated yet extremely flexible data modeling system based on events for back-end applications.

Amir Alesheikh 4 Mar 5, 2023
Prototype risk modeling simulation for Portfolio using Arbiter.

proto-sim Prototype simulation using Arbiter as the simulation & agent engine. Build & Run build.sh cargo run Arbiter config The arbiter.toml config

Primitive 13 Aug 14, 2023