refactor: fix HIGH+MEDIUM architectural violations from code review

HIGH: fix watch_medium data-loss bug, standardize error handling on
ApiError, fix dep direction (rss/template-askama no longer dep on
application), extract ImageFetcher port (remove reqwest from app layer),
move event construction from save_review to use case, extract
infra-wiring crate (DbPool/EventBusBackend dedup), deduplicate
presentation helpers (encode_error, export streaming, multipart parsing)

MEDIUM: split LocalApContentQuery god-trait 10→3 methods, dedup movie
resolution orchestration, add RemoteActorDto/PersonDto mappers, move
AppConfig to infra-wiring, fix SocialQueryPort Uuid→UserId, replace
stringly-typed api-types with domain enums, move count_reviews_in_year
to StatsRepository, dedup event publisher cfg blocks, extract
should_enrich, move group_by_month to application, dedup
count_local_posts, add FederationFlags Default, TUI input helper +
ShowError rename + typed auth errors, api-types cleanup
(UserSettingsDto/UserProfileBase/PreviewRowData)

102 files changed, -681 lines net
This commit is contained in:
2026-07-10 02:08:39 +02:00
parent 26152660bb
commit 12da356a40
110 changed files with 1399 additions and 1867 deletions

View File

@@ -416,7 +416,8 @@ impl DiaryRepository for PostgresDiaryRepository {
"SELECT id, movie_id, user_id, rating, comment,
to_char(watched_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS') AS watched_at,
to_char(created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS') AS created_at,
remote_actor_url
remote_actor_url,
watch_medium
FROM reviews WHERE movie_id = $1 ORDER BY watched_at ASC",
)
.bind(&id_str)
@@ -464,7 +465,8 @@ impl DiaryRepository for PostgresDiaryRepository {
r.id AS review_id, r.movie_id, r.user_id, r.rating, r.comment,
to_char(r.watched_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS') AS watched_at,
to_char(r.created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS') AS created_at,
r.remote_actor_url
r.remote_actor_url,
r.watch_medium
FROM reviews r
INNER JOIN movies m ON m.id = r.movie_id
WHERE r.user_id = $1

View File

@@ -122,10 +122,6 @@ impl GoalRepository for PostgresGoalRepository {
rows.iter().map(row_to_goal).collect()
}
async fn count_reviews_in_year(&self, user_id: &UserId, year: u16) -> Result<u32, DomainError> {
count_reviews_in_year(&self.pool, user_id, year).await
}
}
pub(crate) async fn count_reviews_in_year(

View File

@@ -1,7 +1,6 @@
use async_trait::async_trait;
use domain::{
errors::DomainError,
events::DomainEvent,
models::{Review, ReviewSource},
ports::ReviewRepository,
value_objects::{ReviewId, UserId},
@@ -27,7 +26,7 @@ impl PostgresReviewRepository {
#[async_trait]
impl ReviewRepository for PostgresReviewRepository {
async fn save_review(&self, review: &Review) -> Result<DomainEvent, DomainError> {
async fn save_review(&self, review: &Review) -> Result<(), DomainError> {
let id = review.id().value().to_string();
let movie_id = review.movie_id().value().to_string();
let user_id = review.user_id().value().to_string();
@@ -57,13 +56,7 @@ impl ReviewRepository for PostgresReviewRepository {
.await
.map_err(Self::map_err)?;
Ok(DomainEvent::ReviewLogged {
review_id: review.id().clone(),
movie_id: review.movie_id().clone(),
user_id: review.user_id().clone(),
rating: review.rating().clone(),
watched_at: *review.watched_at(),
})
Ok(())
}
async fn get_review_by_id(&self, review_id: &ReviewId) -> Result<Option<Review>, DomainError> {

View File

@@ -95,6 +95,10 @@ impl StatsRepository for PostgresStatsRepository {
})
}
async fn count_reviews_in_year(&self, user_id: &UserId, year: u16) -> Result<u32, DomainError> {
crate::goals::count_reviews_in_year(&self.pool, user_id, year).await
}
async fn get_user_trends(&self, user_id: &UserId) -> Result<UserTrends, DomainError> {
let uid = user_id.value().to_string();