141 lines
6.5 KiB
Plaintext
141 lines
6.5 KiB
Plaintext
---
|
|
title: Movies Diary — Hexagonal Architecture
|
|
---
|
|
graph TB
|
|
subgraph Binaries["Binaries (Composition Root)"]
|
|
WEB["presentation<br/><i>Axum web server</i><br/>Routes, Handlers, Mappers"]
|
|
WORKER["worker<br/><i>Event consumer</i><br/>Concurrent dispatch, graceful shutdown"]
|
|
TUI["tui<br/><i>Terminal UI</i>"]
|
|
end
|
|
|
|
subgraph Application["Application Layer"]
|
|
direction TB
|
|
CTX["AppContext<br/><i>Repositories + Services</i>"]
|
|
APP_PORTS["ReviewLogger<br/><i>application-layer port</i>"]
|
|
subgraph UseCases["Use Cases"]
|
|
UC_AUTH["auth<br/>login, register"]
|
|
UC_DIARY["diary<br/>log_review, get_diary,<br/>get_activity_feed, export"]
|
|
UC_MOVIES["movies<br/>get_movies, get_movie_profile,<br/>enrich_movie, request_enrichment,<br/>sync_poster, reindex_search"]
|
|
UC_IMPORT["import<br/>create_session, apply_mapping,<br/>execute, profiles"]
|
|
UC_USERS["users<br/>get_users, get_profile,<br/>update_profile"]
|
|
UC_WATCHLIST["watchlist<br/>add, remove, get"]
|
|
UC_WRAPUP["wrapup<br/>generate, compute,<br/>list, delete"]
|
|
UC_GOALS["goals<br/>create, update, delete,<br/>get, list"]
|
|
UC_INTEGRATIONS["integrations<br/>webhooks, watch_queue,<br/>confirm, dismiss"]
|
|
UC_SEARCH["search<br/>execute"]
|
|
UC_PERSON["person<br/>get, get_credits"]
|
|
end
|
|
subgraph EventHandlers["Event Handlers"]
|
|
EH_ENRICH["EnrichmentHandler"]
|
|
EH_DISCOVER["MovieDiscoveryIndexer"]
|
|
EH_CLEANUP["SearchCleanupHandler"]
|
|
EH_REINDEX["SearchReindexHandler"]
|
|
EH_WRAPUP["WrapUpEventHandler"]
|
|
end
|
|
subgraph Jobs["Periodic Jobs"]
|
|
JOB_IMPORT["ImportSessionCleanup"]
|
|
JOB_WATCH["WatchEventCleanup"]
|
|
JOB_STALE["EnrichmentStaleness"]
|
|
JOB_WRAPGEN["WrapUpAutoGenerate"]
|
|
end
|
|
WORKER_SVC["WorkerService<br/><i>Semaphore(8), JoinSet,<br/>shutdown signal</i>"]
|
|
end
|
|
|
|
subgraph Domain["Domain Layer (0 dependencies)"]
|
|
direction TB
|
|
subgraph Models["Models"]
|
|
M_MOVIE["Movie, MovieSummary,<br/>MovieProfile"]
|
|
M_REVIEW["Review, DiaryEntry,<br/>FeedEntry"]
|
|
M_USER["User, UserSummary"]
|
|
M_PERSON["Person, PersonId,<br/>PersonCredits"]
|
|
M_WATCHLIST["WatchlistEntry,<br/>WatchEvent"]
|
|
M_GOAL["Goal, GoalWithProgress,<br/>UserSettings, RemoteGoalEntry"]
|
|
M_WRAPUP["WrapUpReport,<br/>MovieRef, PersonStat"]
|
|
M_SEARCH["SearchQuery,<br/>SearchResults"]
|
|
end
|
|
subgraph Ports["Port Traits (Interfaces)"]
|
|
P_REPOS["MovieRepository<br/>ReviewRepository<br/>DiaryRepository<br/>UserRepository<br/>WatchlistRepository<br/>WatchEventRepository<br/>WebhookTokenRepository<br/>ImportSessionRepository<br/>MovieProfileRepository<br/>WrapUpRepository<br/>GoalRepository<br/>UserSettingsRepository"]
|
|
P_SERVICES["AuthService<br/>MetadataClient<br/>PosterFetcherClient<br/>ObjectStorage<br/>EventPublisher<br/>EventConsumer<br/>PasswordHasher<br/>DiaryExporter<br/>DocumentParser"]
|
|
P_SEARCH["SearchPort<br/>SearchCommand<br/>PersonQuery<br/>PersonCommand"]
|
|
P_FEDERATION["SocialQueryPort<br/>LocalApContentQuery<br/>RemoteWatchlistRepository<br/>RemoteGoalRepository"]
|
|
end
|
|
subgraph DomainServices["Services (pure, no I/O)"]
|
|
DS_WRAPUP["WrapUpAnalyzer<br/><i>build_report, compute_*</i>"]
|
|
DS_REVIEW["ReviewHistoryAnalyzer<br/><i>rating_trend</i>"]
|
|
end
|
|
EVENTS["DomainEvent enum<br/><i>ReviewLogged, MovieDiscovered,<br/>GoalCreated, GoalUpdated,<br/>SearchReindexRequested, ...</i>"]
|
|
VO["Value Objects<br/><i>MovieId, UserId, Rating,<br/>Email, Username, Password, ...</i>"]
|
|
end
|
|
|
|
subgraph ApiTypes["api-types (0 domain deps)"]
|
|
DTO["DTOs<br/><i>MovieDto, ReviewDto,<br/>FeedEntryDto, UserSummaryDto,<br/>CastMemberDto, ...</i>"]
|
|
end
|
|
|
|
subgraph Adapters["Adapters (implement Port Traits)"]
|
|
direction TB
|
|
subgraph Storage["Storage"]
|
|
A_SQLITE["sqlite<br/><i>SQLite repos</i>"]
|
|
A_PG["postgres<br/><i>PostgreSQL repos</i>"]
|
|
A_SQLITE_SEARCH["sqlite-search<br/><i>FTS5</i>"]
|
|
A_PG_SEARCH["postgres-search<br/><i>tsvector/GIN</i>"]
|
|
A_OBJ["object-storage<br/><i>S3 / filesystem</i>"]
|
|
end
|
|
subgraph Messaging["Messaging"]
|
|
A_NATS["nats<br/><i>JetStream / Core</i>"]
|
|
A_PG_QUEUE["postgres-event-queue<br/><i>Polling, dead-letter</i>"]
|
|
A_PAYLOAD["event-payload<br/><i>Serde (de)serialization</i>"]
|
|
end
|
|
subgraph External["External Services"]
|
|
A_METADATA["metadata<br/><i>TMDB search/details</i>"]
|
|
A_TMDB["tmdb-enrichment<br/><i>Credits, keywords, cast</i>"]
|
|
A_POSTER["poster-fetcher<br/><i>TMDB image download</i>"]
|
|
A_AUTH["auth<br/><i>JWT tokens</i>"]
|
|
end
|
|
subgraph Federation["Federation (feature-gated)"]
|
|
A_AP["activitypub<br/><i>k_ap library</i>"]
|
|
A_SQLITE_FED["sqlite-federation"]
|
|
A_PG_FED["postgres-federation"]
|
|
end
|
|
subgraph Media["Media Processing"]
|
|
A_IMG["image-converter<br/><i>AVIF/WebP</i>"]
|
|
A_POSTER_SYNC["poster-sync"]
|
|
end
|
|
subgraph Presentation["Presentation Helpers"]
|
|
A_TEMPLATE["template-askama<br/><i>HTML templates</i>"]
|
|
A_RSS["rss<br/><i>Feed generation</i>"]
|
|
A_EXPORT["export<br/><i>CSV/JSON diary</i>"]
|
|
A_IMPORT["importer<br/><i>CSV/JSON/XLSX parser</i>"]
|
|
end
|
|
subgraph Webhooks["Webhook Parsers"]
|
|
A_JELLYFIN["jellyfin"]
|
|
A_PLEX["plex"]
|
|
end
|
|
end
|
|
|
|
%% Dependency arrows
|
|
WEB -->|"uses"| Application
|
|
WEB -->|"maps to"| ApiTypes
|
|
WORKER -->|"uses"| Application
|
|
|
|
Application -->|"depends on"| Domain
|
|
UC_WRAPUP -->|"delegates to"| DS_WRAPUP
|
|
|
|
Adapters -.->|"implements"| Ports
|
|
|
|
%% Key data flows
|
|
WEB ===|"HTTP"| DTO
|
|
WORKER ===|"Events"| EVENTS
|
|
|
|
classDef domain fill:#1a1a2e,stroke:#e94560,color:#fff
|
|
classDef app fill:#16213e,stroke:#0f3460,color:#fff
|
|
classDef adapter fill:#0f3460,stroke:#533483,color:#fff
|
|
classDef binary fill:#533483,stroke:#e94560,color:#fff
|
|
classDef api fill:#2a2a4a,stroke:#e94560,color:#fff
|
|
|
|
class Domain domain
|
|
class DomainServices domain
|
|
class Application app
|
|
class Adapters adapter
|
|
class Binaries binary
|
|
class ApiTypes api
|