arch: split ConfigRepository, extract polling, consolidate conversions, decouple protocol

- Value↔JSON: From impls on domain Value behind `json` feature, delete 4 duplicate converters
- ConfigRepository split into ConfigRepository (12), UserRepository (3), WidgetStateCache (2)
- polling orchestration moved from bootstrap to application::polling_service
- WidgetRenderer in client-domain owns scroll/cache, both clients use it
- network loop consolidated into client-application::run_connection_loop
- protocol crate drops domain dep, Wire↔Domain conversions move to adapters
This commit is contained in:
2026-06-19 18:12:50 +02:00
parent 1c854d127f
commit 7001b5e911
46 changed files with 1063 additions and 951 deletions

View File

@@ -12,7 +12,8 @@ pub use entities::{
pub use events::DomainEvent;
pub use ports::{
AuthPort, BroadcastPort, ClientRegistry, ConfigRepository, ConnectedClient, DataSourcePort,
EventPublisher, PasswordHashPort, SecretStore, WidgetStateReader,
EventPublisher, PasswordHashPort, SecretStore, UserRepository, WidgetStateCache,
WidgetStateReader,
};
pub use value_objects::{
AlignItems, ContainerNode, Direction, DisplayHint, DisplayHintKind, HAlign, JustifyContent,

View File

@@ -1,7 +1,7 @@
use crate::entities::{
DataSource, DataSourceId, LayoutPreset, LayoutPresetId, User, WidgetConfig, WidgetId,
DataSource, DataSourceId, LayoutPreset, LayoutPresetId, WidgetConfig, WidgetId,
};
use crate::value_objects::{Layout, ThemeConfig, WidgetState};
use crate::value_objects::{Layout, ThemeConfig};
use std::future::Future;
pub trait ConfigRepository {
@@ -56,19 +56,4 @@ pub trait ConfigRepository {
&self,
theme: &ThemeConfig,
) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn get_user_by_username(
&self,
username: &str,
) -> impl Future<Output = Result<Option<User>, Self::Error>> + Send;
fn save_user(&self, user: &User) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn count_users(&self) -> impl Future<Output = Result<u32, Self::Error>> + Send;
fn save_widget_states(
&self,
states: &[(WidgetId, WidgetState)],
) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn load_widget_states(
&self,
) -> impl Future<Output = Result<Vec<(WidgetId, WidgetState)>, Self::Error>> + Send;
}

View File

@@ -5,6 +5,8 @@ mod config_repository;
mod data_source_port;
mod event;
mod secret_store;
mod user_repository;
mod widget_state_cache;
mod widget_state_reader;
pub use auth::{AuthPort, PasswordHashPort};
@@ -14,4 +16,6 @@ pub use config_repository::ConfigRepository;
pub use data_source_port::DataSourcePort;
pub use event::EventPublisher;
pub use secret_store::SecretStore;
pub use user_repository::UserRepository;
pub use widget_state_cache::WidgetStateCache;
pub use widget_state_reader::WidgetStateReader;

View File

@@ -0,0 +1,13 @@
use crate::entities::User;
use std::future::Future;
pub trait UserRepository {
type Error;
fn get_user_by_username(
&self,
username: &str,
) -> impl Future<Output = Result<Option<User>, Self::Error>> + Send;
fn save_user(&self, user: &User) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn count_users(&self) -> impl Future<Output = Result<u32, Self::Error>> + Send;
}

View File

@@ -0,0 +1,15 @@
use crate::entities::WidgetId;
use crate::value_objects::WidgetState;
use std::future::Future;
pub trait WidgetStateCache {
type Error;
fn save_widget_states(
&self,
states: &[(WidgetId, WidgetState)],
) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn load_widget_states(
&self,
) -> impl Future<Output = Result<Vec<(WidgetId, WidgetState)>, Self::Error>> + Send;
}

View File

@@ -1,5 +1,39 @@
use std::collections::BTreeMap;
#[cfg(feature = "json")]
impl From<serde_json::Value> for Value {
fn from(json: serde_json::Value) -> Self {
match json {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(b) => Value::Bool(b),
serde_json::Value::Number(n) => Value::Number(n.as_f64().unwrap_or(0.0)),
serde_json::Value::String(s) => Value::String(s),
serde_json::Value::Array(arr) => {
Value::Array(arr.into_iter().map(Into::into).collect())
}
serde_json::Value::Object(map) => {
Value::Object(map.into_iter().map(|(k, v)| (k, v.into())).collect())
}
}
}
}
#[cfg(feature = "json")]
impl From<&Value> for serde_json::Value {
fn from(v: &Value) -> Self {
match v {
Value::Null => serde_json::Value::Null,
Value::Bool(b) => serde_json::Value::Bool(*b),
Value::Number(n) => serde_json::json!(*n),
Value::String(s) => serde_json::Value::String(s.clone()),
Value::Array(arr) => serde_json::Value::Array(arr.iter().map(Into::into).collect()),
Value::Object(map) => {
serde_json::Value::Object(map.iter().map(|(k, v)| (k.clone(), v.into())).collect())
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,