hexagonal refactor
This commit is contained in:
+158
@@ -0,0 +1,158 @@
|
||||
use async_trait::async_trait;
|
||||
use sqlx::SqliteConnection;
|
||||
|
||||
use crate::domain::{Category, DomainResult, GroceryList, Item, PresenceUser, SessionUser, User};
|
||||
|
||||
/// Repositories take `&mut SqliteConnection` (which a `Transaction` derefs to),
|
||||
/// so several repositories can commit together atomically within a single
|
||||
/// transaction coordinated by the unit of work.
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserRepository: Send + Sync {
|
||||
async fn create_user(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
email: String,
|
||||
display_name: String,
|
||||
password_hash: String,
|
||||
) -> DomainResult<User>;
|
||||
async fn find_user_by_email(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
email: String,
|
||||
) -> DomainResult<Option<(User, String)>>;
|
||||
async fn has_users(&self, txn: &mut SqliteConnection) -> DomainResult<bool>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait SessionRepository: Send + Sync {
|
||||
async fn create_session(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
user_id: i64,
|
||||
) -> DomainResult<(String, String)>;
|
||||
async fn session_user(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
session_token: String,
|
||||
) -> DomainResult<Option<SessionUser>>;
|
||||
async fn delete_session(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
session_token: String,
|
||||
) -> DomainResult<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ListRepository: Send + Sync {
|
||||
async fn list_summaries(&self, txn: &mut SqliteConnection) -> DomainResult<Vec<GroceryList>>;
|
||||
async fn create_list(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
name: String,
|
||||
) -> DomainResult<GroceryList>;
|
||||
async fn get_list(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
list_id: i64,
|
||||
) -> DomainResult<Option<GroceryList>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait CategoryRepository: Send + Sync {
|
||||
async fn categories(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
list_id: i64,
|
||||
) -> DomainResult<Vec<Category>>;
|
||||
async fn create_category(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
list_id: i64,
|
||||
name: String,
|
||||
) -> DomainResult<i64>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ItemRepository: Send + Sync {
|
||||
async fn items(&self, txn: &mut SqliteConnection, list_id: i64) -> DomainResult<Vec<Item>>;
|
||||
async fn add_item(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
list_id: i64,
|
||||
name: String,
|
||||
quantity: String,
|
||||
note: String,
|
||||
category_id: Option<i64>,
|
||||
) -> DomainResult<i64>;
|
||||
async fn set_item_checked(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
list_id: i64,
|
||||
item_id: i64,
|
||||
checked: bool,
|
||||
) -> DomainResult<i64>;
|
||||
async fn update_item(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
list_id: i64,
|
||||
item_id: i64,
|
||||
name: String,
|
||||
quantity: String,
|
||||
note: String,
|
||||
category_id: Option<i64>,
|
||||
) -> DomainResult<i64>;
|
||||
async fn delete_item(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
list_id: i64,
|
||||
item_id: i64,
|
||||
) -> DomainResult<i64>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait InvitationRepository: Send + Sync {
|
||||
async fn create_invitation(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
created_by: i64,
|
||||
token: String,
|
||||
) -> DomainResult<i64>;
|
||||
async fn invitation(&self, txn: &mut SqliteConnection, token: String) -> DomainResult<bool>;
|
||||
async fn accept_invitation(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
token: String,
|
||||
) -> DomainResult<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait PasswordHasher: Send + Sync {
|
||||
fn hash(&self, password: &str) -> Result<String, String>;
|
||||
fn verify(&self, password: &str, encoded_hash: &str) -> Result<bool, String>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait TokenGenerator: Send + Sync {
|
||||
fn generate(&self) -> String;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait RealtimeNotifier: Send + Sync {
|
||||
async fn join(&self, list_id: i64, user_id: i64, display_name: String) -> Subscription;
|
||||
async fn leave(&self, list_id: i64, connection_id: &str);
|
||||
async fn publish_list_changed(&self, list_id: i64, revision: i64);
|
||||
async fn presence(&self, list_id: i64) -> Vec<PresenceUser>;
|
||||
}
|
||||
|
||||
pub struct Subscription {
|
||||
pub connection_id: String,
|
||||
pub receiver: tokio::sync::broadcast::Receiver<HubEvent>,
|
||||
pub presence: Vec<PresenceUser>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HubEvent {
|
||||
ListChanged { list_id: i64, revision: i64 },
|
||||
PresenceChanged { list_id: i64 },
|
||||
}
|
||||
Reference in New Issue
Block a user