refactor Ulid und Dataloader

This commit is contained in:
2026-06-06 11:59:20 +02:00
parent 581c8bee88
commit b5182b7f19
54 changed files with 364 additions and 301 deletions

View File

@@ -5,17 +5,17 @@ use std::sync::Arc;
use crate::domain::gruppe::model::Gruppe;
use crate::domain::gruppe::service::Service;
use crate::scalar::Id;
use crate::scalar::Ulid;
pub struct GruppenLoader {
pub pool: sqlx::PgPool,
}
impl Loader<Id> for GruppenLoader {
impl Loader<Ulid> for GruppenLoader {
type Value = Vec<Gruppe>;
type Error = Arc<sqlx::Error>;
async fn load(&self, keys: &[Id]) -> Result<HashMap<Id, Self::Value>, Self::Error> {
async fn load(&self, keys: &[Ulid]) -> Result<HashMap<Ulid, Self::Value>, Self::Error> {
let rows = Service::new(self.pool.clone())
.gruppe_dataloader(keys)
.await?;

View File

@@ -1,2 +1,6 @@
pub mod gruppe;
pub use gruppe::Gruppe;
pub use gruppe::GruppeDataloader;
pub use gruppe::GruppeErstellen;
pub use gruppe::GruppeLoeschen;
pub use gruppe::GruppeUpdate;

View File

@@ -1,8 +1,30 @@
use crate::scalar::{Id, Time};
use sqlx::FromRow;
pub struct Gruppe {
pub id: Id,
use crate::scalar::{Id, Time, Ulid};
pub struct GruppeErstellen {
pub gruppe_id: Id,
pub id: Ulid,
pub gruppenname: String,
pub erstellt_am: Option<Time>,
pub geaendert_am: Option<Time>,
pub erstellt_am: Time,
pub geaendert_am: Time,
}
pub struct GruppeLoeschen {
pub id: Ulid,
}
pub struct GruppeUpdate {
pub id: Ulid,
pub gruppenname: String,
pub geaendert_am: Time,
}
#[derive(Debug, FromRow)]
pub struct GruppeDataloader {
pub r_ulid: Ulid,
pub g_ulid: Ulid,
pub gruppenname: String,
pub erstellt_am: Time,
pub geaendert_am: Time,
}

View File

@@ -1,12 +1,12 @@
use async_graphql::{ComplexObject, SimpleObject};
use crate::scalar::{Id, Time};
use crate::scalar::{Time, Ulid};
#[derive(sqlx::FromRow, SimpleObject, Clone, Debug)]
#[graphql(complex)]
pub struct Gruppe {
/// Die UUID einer Gruppe
pub id: Id,
pub id: Ulid,
/// Der Gruppenname
pub gruppenname: String,

View File

@@ -1,9 +1,9 @@
use async_graphql::InputObject;
use crate::scalar::Id;
use crate::scalar::Ulid;
#[derive(InputObject)]
pub struct GruppeLoeschenInput {
/// Die ID einer Gruppe
pub id: Id,
/// Die ULID einer Gruppe
pub id: Ulid,
}

View File

@@ -1,10 +1,10 @@
use crate::scalar::Id;
use crate::scalar::Ulid;
use async_graphql::InputObject;
#[derive(InputObject)]
pub struct GruppeUpdateInput {
/// Die ID einer Gruppe
pub id: Id,
/// Die Ulid einer Gruppe
pub id: Ulid,
/// Der Name einer Gruppe
pub gruppenname: String,

View File

@@ -1,48 +1,53 @@
use itertools::Itertools;
use std::collections::HashMap;
use std::sync::Arc;
use itertools::Itertools;
use super::Repository;
use crate::database::Queryer;
use crate::domain::gruppe::entity::GruppeDataloader;
use crate::domain::gruppe::model::Gruppe;
use crate::scalar::Id;
use crate::scalar::Ulid;
impl Repository {
pub async fn gruppe_dataloader<'c, C: Queryer<'c>>(
&self,
db: C,
keys: &[Id],
) -> Result<HashMap<Id, Vec<Gruppe>>, Arc<sqlx::Error>> {
let rows = sqlx::query!(
r#"
SELECT
rg.rolle_id,
g.id,
g.gruppenname,
g.erstellt_am,
g.geaendert_am
FROM gruppen AS g
LEFT JOIN rollen_gruppen AS rg ON g.id = rg.gruppe_id
WHERE rg.rolle_id = ANY($1);
"#,
keys
)
.fetch_all(db)
.await?
.into_iter()
.map(|row| {
(
row.rolle_id,
Gruppe {
id: row.id,
gruppenname: row.gruppenname,
erstellt_am: row.erstellt_am,
geaendert_am: row.geaendert_am,
},
)
})
.into_group_map();
keys: &[Ulid],
) -> Result<HashMap<Ulid, Vec<Gruppe>>, Arc<sqlx::Error>> {
const QUERY: &str = r#"
SELECT
rg.rolle_id as r_ulid,
g.id as g_ulid,
g.gruppenname,
g.erstellt_am,
g.geaendert_am
FROM gruppen AS g
LEFT JOIN rollen_gruppen AS rg ON g.gruppe_id = rg.gruppe_id
WHERE rg.rolle_id IN (
SELECT benutzer_id
FROM benutzer
WHERE id = ANY($1)
);
"#;
let rows = sqlx::query_as::<_, GruppeDataloader>(QUERY)
.bind(keys)
.fetch_all(db)
.await?
.into_iter()
.map(|row| {
(
row.r_ulid,
Gruppe {
id: row.g_ulid,
gruppenname: row.gruppenname,
erstellt_am: row.erstellt_am,
geaendert_am: row.geaendert_am,
},
)
})
.into_group_map();
Ok(rows)
}

View File

@@ -8,19 +8,20 @@ impl Repository {
pub async fn gruppe_erstellen<'c, C: Queryer<'c>>(
&self,
db: C,
gruppe: &entity::Gruppe,
gruppe: &entity::GruppeErstellen,
) -> Result<model::Gruppe, Error> {
const QUERY: &str = r#"
INSERT INTO gruppen (id, erstellt_am, geaendert_am, gruppenname) VALUES (
$1, $2, $3, $4
) RETURNING id, erstellt_am, geaendert_am, gruppenname;
INSERT INTO gruppen (gruppe_id, id, gruppenname, erstellt_am, geaendert_am) VALUES (
$1, $2, $3, $4, $5
) RETURNING id, gruppenname, erstellt_am, geaendert_am;
"#;
let gruppe = sqlx::query_as::<_, model::Gruppe>(QUERY)
.bind(gruppe.gruppe_id)
.bind(gruppe.id)
.bind(&gruppe.gruppenname)
.bind(gruppe.erstellt_am)
.bind(gruppe.geaendert_am)
.bind(&gruppe.gruppenname)
.fetch_one(db)
.await?;

View File

@@ -8,7 +8,7 @@ impl Repository {
pub async fn gruppe_loeschen<'c, C: Queryer<'c>>(
&self,
db: C,
gruppe: &entity::Gruppe,
gruppe: &entity::GruppeLoeschen,
) -> Result<model::Gruppe, Error> {
const QUERY: &str = r#"
DELETE FROM gruppen WHERE id=$1 RETURNING id, gruppenname, erstellt_am, geaendert_am;

View File

@@ -8,12 +8,12 @@ impl Repository {
pub async fn gruppe_update<'c, C: Queryer<'c>>(
&self,
db: C,
gruppe: &entity::Gruppe,
gruppe: &entity::GruppeUpdate,
) -> Result<model::Gruppe, Error> {
const QUERY: &str = r#"
UPDATE gruppen
SET geaendert_am = $2, ruppenname = $3 WHERE gruppeid = $1
RETURNING id, geaendert_am, erstellt_am, gruppenname;
RETURNING id, gruppenname, geaendert_am, erstellt_am;
"#;
let gruppe = sqlx::query_as::<_, model::Gruppe>(QUERY)

View File

@@ -1,14 +1,14 @@
use std::{collections::HashMap, sync::Arc};
use crate::{domain::gruppe::model::Gruppe, scalar::Id};
use crate::{domain::gruppe::model::Gruppe, scalar::Ulid};
use super::Service;
impl Service {
pub async fn gruppe_dataloader(
&self,
keys: &[Id],
) -> Result<HashMap<Id, Vec<Gruppe>>, Arc<sqlx::Error>> {
keys: &[Ulid],
) -> Result<HashMap<Ulid, Vec<Gruppe>>, Arc<sqlx::Error>> {
let gruppen_dataloader = self.repo.gruppe_dataloader(&self.db, keys).await?;
Ok(gruppen_dataloader)
}

View File

@@ -1,23 +1,25 @@
use super::Service;
use crate::{
domain::gruppe::{
entity,
model::{self, GruppeErstelleInput},
},
scalar::Ulid,
};
use anyhow::Error;
use chrono::Utc;
use ulid::Ulid;
use super::Service;
use crate::domain::gruppe::{
entity,
model::{self, GruppeErstelleInput},
};
impl Service {
pub async fn gruppe_erstellen(
&self,
input: GruppeErstelleInput,
) -> Result<model::Gruppe, Error> {
let gruppe_input = entity::Gruppe {
id: Ulid::new().into(),
let gruppe_input = entity::GruppeErstellen {
gruppe_id: ulid::Ulid::new().into(),
id: Ulid(ulid::Ulid::new().into()),
gruppenname: input.gruppenname,
erstellt_am: Some(Utc::now()),
geaendert_am: Some(Utc::now()),
erstellt_am: Utc::now(),
geaendert_am: Utc::now(),
};
let gruppe = self.repo.gruppe_erstellen(&self.db, &gruppe_input).await?;

View File

@@ -1,5 +1,4 @@
use anyhow::Error;
use chrono::Utc;
use super::Service;
use crate::domain::gruppe::{
@@ -12,12 +11,7 @@ impl Service {
&self,
input: GruppeLoeschenInput,
) -> Result<model::Gruppe, Error> {
let gruppe_input = entity::Gruppe {
id: input.id,
gruppenname: String::new(),
erstellt_am: None,
geaendert_am: Some(Utc::now()),
};
let gruppe_input = entity::GruppeLoeschen { id: input.id };
let gruppe = self.repo.gruppe_loeschen(&self.db, &gruppe_input).await?;
Ok(gruppe)

View File

@@ -1,6 +1,5 @@
use anyhow::Error;
use chrono::Utc;
use ulid::Ulid;
use super::Service;
use crate::domain::gruppe::{
@@ -10,11 +9,10 @@ use crate::domain::gruppe::{
impl Service {
pub async fn gruppe_update(&self, input: GruppeUpdateInput) -> Result<model::Gruppe, Error> {
let gruppe_input = entity::Gruppe {
id: Ulid::new().into(),
let gruppe_input = entity::GruppeUpdate {
id: input.id,
gruppenname: input.gruppenname,
erstellt_am: None,
geaendert_am: Some(Utc::now()),
geaendert_am: Utc::now(),
};
let gruppe = self.repo.gruppe_update(&self.db, &gruppe_input).await?;