Перейти к содержанию

Architecture map — polit-media-radar (er)🔗

Living Architecture Map — pilot. This page is the single entry point to the visual architecture of the project: the modules, how they depend on each other, and which module writes which table.

This map has two layers (Living Documentation terminology):

  • Reference layer (generated, cannot lie). Produced mechanically from the code and the live database — it drifts only when the code/schema drift, and a drift gate catches that. → module graph, ER: legacy, ER: SocialScan, ER: context bridge.
  • Evergreen layer (curated, holds intent). Written by a human/agent to explain why — the seams and this overview. → seams: module → table, this file.

Single source of truth + reference. Generated facts are never re-typed into prose here; they are linked. If a number or an edge matters, follow the link to the diagram that owns it.


1. Introduction & Goals🔗

polit-media-radar monitors political-media activity (Telegram, VK) for a set of tracked candidates: it ingests posts ("articles"), comments, reactions and forwards, then layers SocialScan pulls and LLM classification on top to produce the weekly "narodnaya" analytics reports. (Product context: concept-doc-2026-05-06.md; current-state audit: audit-2026-05-19.md — both in docs/architecture/, outside this showcase build.)

Goal of this map: let a reader see the architecture at a glance — modules, modularity, and the seams between modules and the database — without holding it all in their head or reading the code.

2. Building Block View🔗

The codebase is 12 top-level packages under polit_media_radar (import root src/). The dependency graph — generated by tach from real imports — is in diagrams/modules.md. Read it there; the shape in one sentence: cli is the composition root that wires everything; domain, security, config are leaf/utility layers; ingestion and reports sit on top of persistence. There is one honest cli ↔ scheduler cycle.

3. Runtime & Deployment View🔗

The system runs as two Docker Compose services — postgres (schema radar) and app — plus scheduled jobs (Forgejo/GitHub Actions). The ingestion pipeline (ingestion) pulls sources, persists through the repository layer (persistence), and the reporting modules (reports, services) read back to build the weekly report. The database is a single PostgreSQL 16 container (C4 nomenclature: the DB is a Container; the ER diagrams below detail it).

4. Data View — the database container🔗

The 34 base tables of schema radar split into two bounded contexts, drawn separately so neither becomes an unreadable tangle:

How SocialScan attaches to the spine (the split would otherwise hide this): two legacy tables — candidates (the actor) and fetch_runs (one ingestion run) — are the shared spine. ss_snapshots anchors a SocialScan pull to an ingestion run via source_run_id → fetch_runs.id; the per-actor SocialScan leaf tables carry candidate_id → candidates.id directly. Full edge list: context-bridge.md.

5. Cross-cutting Concepts🔗

  • The spine. candidates and fetch_runs are referenced from both contexts; they are the integration backbone. They appear (styled) in both ER diagrams by design.
  • Writer-pending skeletons. Four repositories exist as NotImplementedError skeletons — their tables are in the schema but nothing writes them yet (ss_themes, ss_recommendations, ss_judge_results, ss_wave_runs). They are marked, not drawn as live seams, in seams.md.
  • Writes that bypass the repository layer. Two tables (ss_post_tonality, weekly_corpus_summary) are written directly from modules, and article_restrictions has no writer at all. These deviations from the clean module → repository → table pattern are listed in seams.md and accepted as exceptions with criteria in ADR-0018. (ss_channel_followers_weekly was the one real erosion — two writers + a reader — and was refactored behind SqlAlchemySsChannelFollowersWeeklyRepository.)
  • Live schema vs. ORM. The ER diagrams come from the live database, not the ORM, so ss_trends_daily (a raw-SQL table with no ORM model) appears — an ORM-only tool would miss it.

6. Architecture Decisions🔗

The "why" behind structural choices lives in docs/adr/ and the dated decisions-log-*.md files in this directory — not duplicated here.


How to read this map — the go/no-go trace (SC-001)🔗

Follow one seam end to end without opening any code:

ingestionSqlAlchemyArticleRepositoryarticles

  1. Open modules.md: ingestion is a module, and it depends on persistence (where repositories live).
  2. Open seams.md: the ingestion → articles edge is labelled ArticleRepository (a writer, solid arrow). The exhaustive table confirms ArticleRepository writes articles and reads article_revisions.
  3. Open db/legacy.md: articles is there, with candidate_id → candidates (the spine) — so you can see what an article connects to.

Three clicks, module to table, no source code. That chain is the pilot's success criterion.


Pilot run: 2026-06-22. Scope: project er only. Generated by tach + tbls against the live schema; see arch-map-pilot-run-2026-06-22.md (in docs/architecture/) for the pilot run report.

Productionized 2026-06-22: the generated layer is now protected by a CI drift gate (.forgejo/workflows/arch-drift.yml + scripts/arch_map.py), surfaced as a MkDocs Material showcase (root mkdocs.yml, deploy via .forgejo/workflows/docs.yml), and regenerable via the arch-map skill. The one real erosion (ss_channel_followers_weekly) was refactored behind a repository; skeleton repos and the remaining bypasses are accepted exceptions in ADR-0018. Full report: arch-map-prod-run-2026-06-22.md.

Re-homed 2026-06-24: the showcase moved off the deprecated delphi.antopkin.ru/docs/ placeholder onto the dedicated docs hub docs.antopkin.ru — this map lives at docs.antopkin.ru/polit-media-radar/, the first tenant of a per-project sub-path hub. Hosting/deploy details: docs/ops/docs-site-deploy.md; full report: docs-hub-run-2026-06-24.md.