182 lines
13 KiB
Markdown
182 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
|
|
model interpretation belong to `readings.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.
|