refactor: migrate monolithic crate to Cargo Workspace with Clean Architecture

Transform the single binary crate into a 9-crate workspace monorepo:

- Root Cargo.toml as [workspace] manager with resolver = "2"
- zesdex-entities: Domain entity types (session, settings, store, message, etc.)
- zesdex-utils: Pure utility functions (error, logger, pagination, slug, clipboard)
- zesdex-dto: Data Transfer Objects for LLM provider API communication
- zesdex-ipc: Unix-socket IPC layer (client/server/framing/protocol)
- zesdex-iam: Identity & Access Management (Clean Architecture: domain/application/infrastructure)
- zesdex-cms: Content Management (Clean Architecture: domain/application/infrastructure)
- zesdex-middleware: HTTP middleware (Auth, CORS, Rate Limiting)
- zesdex-libs: Composition root (AppContext, DB init, JWT, Argon2)
- zesdex-backend: Main binary entry point + seed/migrate binaries
- DevOps: Dockerfile, docker-compose, Nix (flake/shell/default), CI/CD updates
- Remove dead root src/ and src-misc/ directories

All crate re-exports maintain backward compatibility with original
crate::model::*, crate::dto::*, crate::ipc::* module paths.
Feature crates enforce strict layer separation: domain -> application
-> infrastructure with generic trait-based dependency injection.
This commit is contained in:
asepharyana
2026-07-17 09:08:41 +07:00
parent 86cc412395
commit be0a9582bb
248 changed files with 7901 additions and 1505 deletions
@@ -0,0 +1,76 @@
//! Repository traits — pure abstraction boundaries for persistence.
//!
//! Each trait defines load / save / query operations that infrastructure
//! adapters implement. The domain and application layers depend only on
//! these traits, never on concrete persistence implementations.
#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_possible_wrap
)]
use std::path::Path;
use anyhow::Result;
use super::app_config::AppConfig;
use super::conversation::Conversation;
use super::edit_log::{EditLog, EditLogEntry};
use super::memory::Memory;
use super::settings::Settings;
/// Persistence contract for `Settings`.
pub trait SettingsRepository {
/// Load settings from a base directory.
fn load(&self, base_dir: &Path) -> Result<Settings>;
/// Save settings to a base directory.
fn save(&self, base_dir: &Path, settings: &Settings) -> Result<()>;
}
/// Persistence contract for `AppConfig`.
pub trait AppConfigRepository {
/// Load app config from a base directory.
fn load(&self, base_dir: &Path) -> Result<AppConfig>;
/// Save app config to a base directory.
fn save(&self, base_dir: &Path, config: &AppConfig) -> Result<()>;
}
/// Persistence contract for `Conversation`.
pub trait ConversationRepository {
/// Load a conversation from a session directory.
fn load(&self, session_dir: &Path) -> Result<Conversation>;
/// Save a conversation to a session directory.
fn save(&self, session_dir: &Path, conversation: &Conversation) -> Result<()>;
}
/// Persistence contract for `Memory`.
pub trait MemoryRepository {
/// List all memory slugs in a memory directory.
fn list(&self, memory_dir: &Path) -> Result<Vec<String>>;
/// Load a single memory by name.
fn load(&self, memory_dir: &Path, name: &str) -> Result<Memory>;
/// Save (create or update) a memory.
fn save(&self, memory_dir: &Path, memory: &Memory) -> Result<()>;
/// Delete a memory by name.
fn delete(&self, memory_dir: &Path, name: &str) -> Result<()>;
}
/// Persistence contract for `EditLog`.
pub trait EditLogRepository {
/// Open (or start tracking) the edit log for a session directory.
fn open(&self, session_dir: &Path) -> Result<EditLog>;
/// Append one entry, persisting it immediately.
fn append(&self, session_dir: &Path, log: &mut EditLog, entry: EditLogEntry) -> Result<()>;
/// Return a reference to all in-memory entries.
fn entries(&self, log: &EditLog) -> Vec<EditLogEntry>;
}