Files
xiaobaifupan/app/ARCHITECTURE.md
T

13 KiB

Application architecture

app/ is the standalone, behavior-preserving modular source tree accepted by the user on 2026-08-01. It is the only production source boundary and must not read or import a parent checkout, a retired baseline, or a failed implementation.

The application deliberately remains a modular monolith: one Python process, one SQLite WAL database, and a build-free HTML/CSS/JavaScript client. The migration changed source ownership and imports, not the technology stack or observable product behavior.

Runtime path

browser
  -> frontend/shared/api.js
  -> backend HTTP transport and feature HTTP mixins
  -> feature services
  -> repositories / DataGateway / LLMGateway
  -> SQLite / market providers / model providers

background scheduler
  -> backend/jobs
  -> the same feature services and repositories

Source ownership

  • server.py is the stable command/import facade. backend/application.py is the narrow composition root for DashboardService, RequestHandler, and the process-wide service instance; dependency construction remains in backend/bootstrap/.
  • backend/bootstrap/ owns process configuration, dependency construction, startup, and shared input/display-format contracts. It does not own feature behavior.
  • backend/http/ owns common authentication, request IDs, JSON/NDJSON responses, static delivery, streaming connection lifecycle, and error normalization. Feature-specific transport handlers live beside their feature. backend/http/dispatch.py owns only public versus authenticated guard order, named POST dispatch, feature-route traversal, static fallback, and final 404 responses. Exact POST maps live there; endpoint parsing, response fields, and feature-specific exceptions belong to backend/features/<feature>/routes.py.
  • backend/features/<feature>/ owns the mechanically moved service, repository, HTTP, agent, or deterministic calculation code for that product area.
  • backend/data/ owns provider construction, source policy, provenance, units, freshness, coverage, display-versus-calculation eligibility, and shared numeric normalization policies.
  • backend/data/providers/tushare_client.py is the stable public TushareClient facade and owns only its dataclass fields and shared cache state. Tushare HTTP transport belongs to tushare_transport.py; market overview and realtime breadth belong to tushare_dashboard.py; indices belong to tushare_indices.py; Shenwan membership and industry snapshots belong to tushare_industries.py; generic sector snapshots belong to tushare_sectors.py; hot-money and dragon-tiger data belong to tushare_dragon_tiger.py; stock detail and intraday data belong to tushare_stocks.py; trading-calendar, daily, and limit-list access belong to tushare_daily.py; small shared deterministic conversions belong to tushare_helpers.py.
  • backend/database/ owns connection management, ordered migrations, and narrow repository adapters. Root database.py remains the legacy schema/composition anchor and combines the feature repository mixins; do not add feature queries to it.
  • backend/jobs/ owns job definitions, locks, retries, idempotency, and persisted run state. backend/jobs/service.py is the application-facing owner of scheduler start/stop, manual refresh submission, and periodic refresh coordination.
  • backend/llm/ owns model selection, membership/quota checks, fallback, provider transport, streaming rules, and call audit. Feature agents only prepare messages and interpret feature-specific results.
  • frontend/index.html owns only the login layer, application Shell, overview strip, status bar, global dialogs, and the single page-fragment mount point. frontend/bootstrap.js loads the registered page fragments before the unchanged application runtime starts.
  • frontend/pages.config.js is the only runtime owner of page-fragment paths and script execution order. Do not add page scripts directly to index.html or create another loader.
  • frontend/app.js is only the browser startup coordinator: initialize controls, resolve the initial route, start the authenticated application, and invoke registered binding owners. It must not own feature event handlers, dashboard rendering, account/admin behavior, theme behavior, table behavior, or application state definitions.
  • frontend/shared/ is the only browser data-API/state/Shell/component boundary. Within it, context.js owns application state and DOM handles, application.js owns API/Shell/page lifecycle composition, feedback.js owns common feedback and motion, dashboard.js owns market-dashboard refresh and date coordination, session.js owns authentication/account access, admin.js owns system administration, theme.js owns theme switching, and table.js owns generic table behavior. The Bootstrap fetch is limited to registered same-origin static HTML fragments.
  • frontend/pages/ owns page-local markup, behavior, and styles through page.html, page.js, and foundation.css. Each feature registers its own one-time control binder with the page runtime; feature selectors and event handlers must not be added to app.js. The original DOM and runtime were split mechanically during migration. Current maintenance is governed by the runtime registry, unique symbol owners, DOM/API contracts, JavaScript syntax checks, and Playwright behavior rather than embedded historical source ranges.
  • frontend/pages/market/ owns cross-page market presentation through narrow runtime modules: breadth.js, charts.js, entity-detail.js, stock-detail.js, preview.js, search.js, and bindings.js. runtime.js is retired; do not recreate a combined market runtime or a compatibility loader. pages.config.js is the sole owner of their execution order.
  • backend/features/screener/engine.py is the stable screener compatibility facade only. Screener declarations belong to catalog.py; external factor synchronization belongs to data_sync.py; deterministic technical and statistical helpers belong to indicators.py; factor construction belongs to factors.py; formula validation, scoring, and local strategy compilation belong to formula.py; market-phase identification belongs to regime.py; screening execution and result persistence belong to selection.py; historical evaluation belongs to backtest.py.
  • backend/features/heaven/service.py is the stable Wentian service facade only. Manual six-line input validation and safety gates belong to manual.py; trend setup, market mode, source disclosure, and quality checks belong to trend.py; stock, index, and sector context collection belongs to market_context.py; personal fields, hexagrams, saved readings, and interpretation orchestration belong to readings.py; deterministic Jing Fang Na Jia, eight palaces, six relatives, self/response, six spirits, calendar relations, and hidden spirits belong to six_yao.py; source-traceable Wentian knowledge retrieval and the only LLM-bound context projection belong to knowledge.py; prompt construction and answer validation remain in agent.py. These owners cooperate through the composed service object and do not duplicate or delegate method bodies through the facade.
  • Application-facing system credentials, data/LLM status, and administrator settings belong to backend/features/system/service.py; account-context delegation belongs to backend/features/accounts/application.py. They are composed into DashboardService and must not return to the composition root.
  • backend/features/market/insights.py is the stable public MarketInsightsService facade only. Shared construction, trading context, stock master access, and concept parsing belong to insights_context.py; auction scoring and candidate construction belong to insights_auction_scoring.py; auction session, amount history, watchlist enrichment, and live snapshots belong to insights_auction_data.py; auction result orchestration belongs to insights_auction.py; theme library/detail behavior belongs to insights_themes.py; and hot ranking behavior belongs to insights_popularity.py.
  • frontend/shared/tokens.css owns global design semantics. Shared foundations live in frontend/shared/*.css and frontend/shared/components/*.css; page foundations live beside their page in frontend/pages/<feature>/foundation.css. These 22 files replace the retired frontend/styles/styles.css, four historical refinement layers, and the former Wentian page stylesheet. Production loads only this canonical stack: every selector/context pair has one owner, shared roots stay in shared files, and page-scoped rules stay beside their page.
  • config/ is the versioned registry for pages, features, APIs, datasets, quality rules, jobs, and the generated candidate architecture inventory.

The source root has four Python entry modules only: server.py starts and exports the process surface, database.py remains the documented schema/composition anchor, api_access.py owns the route-access registry entry, and sync_data.py is the manual synchronization command. The 19 migration-only import aliases were retired after all internal and test consumers moved to canonical backend/ owners. Do not recreate root-level feature import shims.

Generated local artifacts belong under runtime/: server output in runtime/logs, Python cache in runtime/cache, and browser artifacts in runtime/test-results. Docker continues to emit logs through its configured logging driver instead of writing into the source tree.

Non-negotiable maintenance rules

  1. Preserve account ownership in every user-private query and test it with two accounts.
  2. Browser business-data requests go through frontend/shared/api.js; only frontend/bootstrap.js may fetch registered static page fragments. Provider calls go through the data boundary; model calls go through backend/llm/.
  3. Calculation datasets fail closed when required source, date, unit, freshness, or coverage evidence is missing. Display fallbacks do not silently enter calculations.
  4. Do not create root-level feature compatibility modules; import the canonical backend/ owner directly.
  5. Do not remove uncertain code without reference scanning, old/new differential evidence, browser checks, and manual acceptance.
  6. Run python tools/verify_baseline.py for every change and add --e2e when runtime or frontend behavior can be affected.
  7. Do not recreate late-loading legacy.css, override.css, fix.css, or page-wide patch layers. Change the canonical shared or page owner and keep the CSS ownership tests green.
  8. Do not put workspace-view roots back into frontend/index.html. Add or change page DOM only in its registered frontend/pages/<feature>/page.html, without introducing a second fragment or runtime-script registry.
  9. Do not add feature selectors, feature event listeners, shared state declarations, or shared service implementations to frontend/app.js; extend the existing unique owner and keep the startup-entry boundary tests green.
  10. Do not merge market charts, previews, search, stock details, entity details, breadth, and event binding back into one runtime file. Keep each definition in its registered owner and keep the market runtime ownership test green.
  11. Do not merge screener catalogs, data synchronization, indicators, factor construction, formulas, regime detection, selection, and backtesting back into one engine. Keep backend/features/screener/engine.py as a compatibility facade and preserve one canonical owner for each responsibility.
  12. Do not merge Tushare transport, dashboard, indices, Shenwan industries, sectors, dragon-tiger data, stock detail, and daily-market access back into one client. Keep backend/data/providers/tushare_client.py as the single public class facade, and do not duplicate provider method bodies in that facade or another compatibility module.
  13. Do not merge Wentian manual validation, trend orchestration, market-context collection, and reading/LLM behavior back into one service. Keep backend/features/heaven/service.py as a method-free composition facade and preserve one canonical owner for every Wentian service method.
  14. Do not merge auction scoring, auction data preparation, auction orchestration, themes, popularity, and shared insight context back into one market-insights service. Keep backend/features/market/insights.py as a method-free public facade and preserve one canonical owner for every market-insight method.
  15. Do not put feature route bodies, system settings behavior, account delegation, or job lifecycle methods back into backend/application.py. Keep it as a composition root; keep common HTTP guard/404 behavior in backend/http/dispatch.py; and keep endpoint-specific parsing and responses in the corresponding backend/features/<feature>/routes.py.
  16. Do not add preservation source-range markers, copied historical CSS fragments, or a tool that reconstructs the retired monolithic frontend. Historical maps remain evidence only; current owners and behavior tests are the maintenance boundary.

Current maintenance rules are in AGENTS.md and docs/maintenance/人工维护指南.md. Historical migration constraints and evidence remain under docs/migration/ for audit only.