Attribute Macro ruva_macro::aggregate

source ·
#[aggregate]
Expand description

Define Aggregate root

§Example

#[aggregate]
pub struct TestAggregate {
    pub(crate) age: i64,
}

fn test_aggregate() {
let aggregate = TestAggregate::default().set_age(1);
assert_eq!(aggregate.version, 0);
assert!(!aggregate.is_existing);
assert_eq!(aggregate.events.len(), 0);
assert_eq!(aggregate.age, 1)
}
#[aggregate]
pub struct TestAggregate {
    pub(crate) age: i64,
    pub(crate) name: String,
}

Likewise, not specifying identifier will also error out

#[aggregate]
pub struct TestAggregate {
    pub(crate) age: i64,
    pub(crate) name: String,
}

{your aggregate name}Adapter will be generated automatically so you can use it to adapt it to database

#[aggregate]
pub struct AggregateStruct {
    #[adapter_ignore]
    id: i32,
    #[serde(skip_serializing)]
    name: String,
    some_other_field: i32,
}
let aggregate = AggregateStruct::default();
let serialized = serde_json::to_string(&aggregate).unwrap();
assert_eq!(serialized, "{\"id\":0,\"some_other_field\":0,\"version\":0}");

let adapter = AggregateStructAdapter::default();
let serialized = serde_json::to_string(&adapter).unwrap();
assert_eq!(serialized, "{\"some_other_field\":0}");

§Automatic derive macro

#[derive(Default, Debug, Serialize, Deserialize)] will be automatically added to the struct.

#[aggregate]
pub struct AggregateStruct {
   #[adapter_ignore]
  id: i32,
 #[serde(skip_serializing)]
name: String,
some_other_field: i32,
}

Even if you add Default, Debug, Serialize, Deserialize there won’t be any conflict.

Conversion is automatically done as follows:

let aggregate = AggregateStruct {
        name: "migo".into(),
        some_other_field: 2,
        id: 1,
        ..Default::default()
    };
let converted_adapter = AggregateStructAdapter::from(aggregate);
assert_eq!(converted_adapter.name, "migo");
assert_eq!(converted_adapter.some_other_field, 2);
let converted_struct = AggregateStruct::from(converted_adapter);
assert_eq!(converted_struct.name, "migo");
assert_eq!(converted_struct.some_other_field, 2);

Generic can also be used for aggregate:

#[derive(Default, Debug, Serialize, Deserialize)]
struct Unset;

#[aggregate]
#[derive(Default, Debug, Serialize, Clone)]
struct MyStruct<T = Unset>
where
    T: Send + Sync + Default + 'static,
{
    name: String,
    age: i32,

    #[adapter_ignore]
    sub_type: T,
}

impl MyStruct<String> {
    fn do_something_with_string(&self) -> String {
        self.sub_type.clone()
    }
}

impl MyStruct<i32> {
    fn do_something_with_i32(&self) -> i32 {
        self.sub_type
    }
}

let adapter = MyStructAdapter {
    name: "hello".to_string(),
    age: 10,
};

let _my_unset_struct = Into::<MyStruct>::into(adapter.clone()); // default type is set which has no method attached.

let my_string_struct = Into::<MyStruct<String>>::into(adapter.clone());
let my_int32_struct = Into::<MyStruct<i32>>::into(adapter.clone());

assert_eq!(my_string_struct.do_something_with_string(), String::default());
assert_eq!(my_int32_struct.do_something_with_i32(), i32::default());