fn delete
If you are using Event Sourcing we assume you believe in immutability and keeping a long term audit history.
Deleting data from the Database goes against these principles.
Therefore es-entity
does not provide a way to actually delete data.
It is however possible to configure a soft delete option by marking delete = soft
on the EsRepo
.
This will omit entities that have been flagged as deleted from all queries as well as generate additional queries that can include the deleted entities:
fn find_by_<column>_include_deleted
fn maybe_find_by_<column>_include_deleted
fn list_by_<column>_include_deleted
fn list_for_<column>_by_<cursor>_include_deleted
As a prerequisite the index
table must include a deleted
column:
CREATE TABLE users (
id UUID PRIMARY KEY,
name VARCHAR NOT NULL,
-- deleted will be set to 'TRUE' when `delete` is called.
deleted BOOL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL
);
extern crate es_entity; extern crate sqlx; extern crate serde; extern crate tokio; extern crate anyhow; use serde::{Deserialize, Serialize}; es_entity::entity_id! { UserId } impl IntoEvents<UserEvent> for NewUser { fn into_events(self) -> EntityEvents<UserEvent> { EntityEvents::init( self.id, [UserEvent::Initialized { id: self.id, name: self.name, }], ) } } impl TryFromEvents<UserEvent> for User { fn try_from_events(events: EntityEvents<UserEvent>) -> Result<Self, EsEntityError> { Ok(User { id: events.id().clone(), name: "Delyth".to_string(), events }) } } pub struct NewUser { id: UserId, name: String } use es_entity::*; #[derive(EsEvent, Debug, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] #[es_event(id = "UserId")] pub enum UserEvent { Initialized { id: UserId, name: String }, Deleted } #[derive(EsEntity)] pub struct User { pub id: UserId, name: String, events: EntityEvents<UserEvent>, } impl User { fn delete(&mut self) -> Idempotent<()> { idempotency_guard!( self.events.iter_all(), UserEvent::Deleted ); self.events.push(UserEvent::Deleted); Idempotent::Executed(()) } } #[derive(EsRepo)] #[es_repo( entity = "User", columns(name = "String"), delete = "soft" )] pub struct Users { pool: sqlx::PgPool } async fn init_pool() -> anyhow::Result<sqlx::PgPool> { let pg_con = format!("postgres://user:password@localhost:5432/pg"); Ok(sqlx::PgPool::connect(&pg_con).await?) } #[tokio::main] async fn main() -> anyhow::Result<()> { let users = Users { pool: init_pool().await? }; let new_user = NewUser { id: UserId::new(), name: "Delyth".to_string() }; let mut user = users.create(new_user).await?; let found_user = users.maybe_find_by_name("Delyth").await?; assert!(found_user.is_some()); if user.delete().did_execute() { users.delete(user).await?; } let found_user = users.maybe_find_by_name("Delyth").await?; assert!(found_user.is_none()); let found_user = users.maybe_find_by_name_include_deleted("Delyth").await?; assert!(found_user.is_some()); sqlx::query!(r#" WITH deleted_users AS ( DELETE FROM user_events WHERE id IN (SELECT id FROM users WHERE deleted = true) RETURNING id ) DELETE FROM users WHERE deleted = true"# ).execute(users.pool()).await?; Ok(()) }