Files
xiaobaifupan/app/ARCHITECTURE.md
T

186 lines
13 KiB
Markdown

# 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
```text
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.