Skip to content

Commit

Permalink
Keep the entity declaration in the backend
Browse files Browse the repository at this point in the history
The front end simply needs to understand how to event source state, and we want to keep the entity management at the backend.
  • Loading branch information
huntc committed Sep 29, 2023
1 parent 8b50a2c commit 16b0f51
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 62 deletions.
50 changes: 48 additions & 2 deletions examples/iot-service/backend/src/temperature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

use std::{num::NonZeroUsize, sync::Arc};

use akka_persistence_rs::{
effect::{emit_event, reply, unhandled, Effect, EffectExt},
entity::{Context, EventSourcedBehavior},
};
use akka_persistence_rs::{
entity_manager::{self},
EntityId, Message,
Expand All @@ -14,9 +18,51 @@ use chrono::{DateTime, Utc};
use streambed::commit_log::{ConsumerRecord, Key, ProducerRecord, Topic};
use streambed_confidant::FileSecretStore;
use streambed_logged::{compaction::NthKeyBasedRetention, FileLog};
use tokio::sync::mpsc;
use tokio::sync::{mpsc, oneshot};

pub use iot_service_model::temperature::{Event, SecretDataValue, State};

// Declare temperature sensor entity concerns

pub enum Command {
Get { reply_to: oneshot::Sender<Vec<u32>> },
Post { temperature: u32 },
Register { secret: SecretDataValue },
}

pub struct Behavior;

impl EventSourcedBehavior for Behavior {
type State = State;

type Command = Command;

pub use iot_service_model::temperature::{Behavior, Command, Event, SecretDataValue, State};
type Event = Event;

fn for_command(
_context: &Context,
state: &Self::State,
command: Self::Command,
) -> Box<dyn Effect<Self>> {
match command {
Command::Get { reply_to } if !state.secret.is_empty() => {
reply(reply_to, state.history.clone().into()).boxed()
}

Command::Post { temperature } if !state.secret.is_empty() => {
emit_event(Event::TemperatureRead { temperature }).boxed()
}

Command::Register { secret } => emit_event(Event::Registered { secret }).boxed(),

_ => unhandled(),
}
}

fn on_event(context: &Context, state: &mut Self::State, event: Self::Event) {
state.on_event(context, event);
}
}

// Declare how we marshal our data with FileLog. This is essentially
// down to encoding our event type and entity id to a key used for
Expand Down
6 changes: 2 additions & 4 deletions examples/iot-service/frontend/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use akka_persistence_rs::{
entity::Context as EntityContext, entity::EventSourcedBehavior, entity_manager::EventEnvelope,
EntityId,
entity::Context as EntityContext, entity_manager::EventEnvelope, EntityId,
};
use tokio::sync::{broadcast, mpsc};
use web_sys::HtmlInputElement;
Expand Down Expand Up @@ -88,11 +87,10 @@ impl Component for App {

Message::UpdateTemperature { envelope } => {
if envelope.entity_id == self.entity_id {
temperature::Behavior::on_event(
self.temperature.on_event(
&EntityContext {
entity_id: &envelope.entity_id,
},
&mut self.temperature,
envelope.event,
);
true
Expand Down
4 changes: 2 additions & 2 deletions examples/iot-service/frontend/src/temperature.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Declare the entity
// Declare and query the temperature event source

use akka_persistence_rs::{entity_manager::EventEnvelope, EntityId};
use gloo_net::eventsource::futures::EventSource;
use log::{error, warn};
use tokio::sync::{broadcast, mpsc};
use tokio_stream::StreamExt;

pub use iot_service_model::temperature::{Behavior, Command, Event, SecretDataValue, State};
pub use iot_service_model::temperature::{Event, SecretDataValue, State};

// Execute the event source query for commanded entity ids

Expand Down
74 changes: 20 additions & 54 deletions examples/iot-service/model/src/temperature.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,45 @@
// Handle temperature sensor entity concerns
// Handle temperature sensor state concerns

use std::collections::VecDeque;

use akka_persistence_rs::{
effect::{emit_event, reply, unhandled, Effect, EffectExt},
entity::{Context, EventSourcedBehavior},
};
use akka_persistence_rs::entity::Context;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use tokio::sync::oneshot;

// Declare the entity and its behavior
// Declare the state and how it is to be sourced from events

#[derive(Default)]
pub struct State {
pub history: VecDeque<u32>,
pub secret: SecretDataValue,
}

pub type SecretDataValue = SmolStr;

pub enum Command {
Get { reply_to: oneshot::Sender<Vec<u32>> },
Post { temperature: u32 },
Register { secret: SecretDataValue },
}

#[derive(Clone, Deserialize, Serialize)]
pub enum Event {
Registered { secret: SecretDataValue },
TemperatureRead { temperature: u32 },
}

pub struct Behavior;

const MAX_HISTORY_EVENTS: usize = 10;

impl EventSourcedBehavior for Behavior {
type State = State;

type Command = Command;

type Event = Event;

fn for_command(
_context: &Context,
state: &Self::State,
command: Self::Command,
) -> Box<dyn Effect<Self>> {
match command {
Command::Get { reply_to } if !state.secret.is_empty() => {
reply(reply_to, state.history.clone().into()).boxed()
}
impl State {
// We provide an event sourcing function so that we can
// source state from events whether we are using the entity
// manager or not.

Command::Post { temperature } if !state.secret.is_empty() => {
emit_event(Event::TemperatureRead { temperature }).boxed()
}

Command::Register { secret } => emit_event(Event::Registered { secret }).boxed(),

_ => unhandled(),
}
}

fn on_event(_context: &Context, state: &mut Self::State, event: Self::Event) {
pub fn on_event(&mut self, _context: &Context, event: Event) {
match event {
Event::Registered { secret } => {
state.secret = secret;
self.secret = secret;
}
Event::TemperatureRead { temperature } => {
if state.history.len() == MAX_HISTORY_EVENTS {
state.history.pop_front();
if self.history.len() == MAX_HISTORY_EVENTS {
self.history.pop_front();
}
state.history.push_back(temperature);
self.history.push_back(temperature);
}
}
}
}

pub type SecretDataValue = SmolStr;

#[derive(Clone, Deserialize, Serialize)]
pub enum Event {
Registered { secret: SecretDataValue },
TemperatureRead { temperature: u32 },
}

0 comments on commit 16b0f51

Please sign in to comment.