278 lines
10 KiB
Markdown
278 lines
10 KiB
Markdown
# Application Architecture Standard
|
|
|
|
Status: Accepted
|
|
|
|
Date: 2026-07-29
|
|
|
|
## 1. Objective
|
|
|
|
The application remains a deployable modular monolith while its internal boundaries are made
|
|
explicit. Governance must preserve current functionality, account isolation, visual behavior,
|
|
and Docker simplicity. A structural refactor is not permission to change product behavior.
|
|
|
|
## 2. Target Topology
|
|
|
|
```text
|
|
browser
|
|
-> frontend shared API client
|
|
-> backend HTTP controllers
|
|
-> feature application services
|
|
-> repositories / DataGateway / LLMGateway
|
|
-> SQLite / market providers / model providers
|
|
|
|
background jobs
|
|
-> the same feature application services
|
|
-> the same repositories and gateways
|
|
```
|
|
|
|
The browser and jobs are two delivery mechanisms. Neither owns business rules.
|
|
|
|
## 3. Target Source Layout
|
|
|
|
```text
|
|
frontend/
|
|
shared/
|
|
tokens.css
|
|
shell.js
|
|
api.js
|
|
state.js
|
|
components/
|
|
pages/<feature>/
|
|
page.js
|
|
desktop.css
|
|
mobile.css
|
|
pages.config.js
|
|
|
|
backend/
|
|
bootstrap/
|
|
http/
|
|
router.py
|
|
auth.py
|
|
errors.py
|
|
schemas/
|
|
features/<feature>/
|
|
routes.py
|
|
service.py
|
|
repository.py
|
|
schemas.py
|
|
data/
|
|
gateway.py
|
|
policy.py
|
|
contracts.py
|
|
providers/
|
|
database/
|
|
connection.py
|
|
migrations/
|
|
repositories/
|
|
jobs/
|
|
llm/
|
|
gateway.py
|
|
usage.py
|
|
models.py
|
|
prompts/
|
|
features_config.py
|
|
|
|
tests/
|
|
data/
|
|
server.py
|
|
```
|
|
|
|
The migration may use compatibility facades. Old modules are removed only after all callers
|
|
move and regression gates pass.
|
|
|
|
## 4. Dependency Direction
|
|
|
|
Allowed direction:
|
|
|
|
```text
|
|
HTTP / jobs -> feature services -> ports -> infrastructure adapters
|
|
frontend pages -> shared components/state/API -> backend API
|
|
```
|
|
|
|
Forbidden dependencies:
|
|
|
|
1. Frontend code must not call external market or LLM providers directly.
|
|
2. HTTP controllers must not contain scoring, screening, divination, or persistence logic.
|
|
3. Feature services must not instantiate Tushare, iFinD, Eastmoney, Tencent, SQLite, or LLM
|
|
clients directly.
|
|
4. Provider adapters must not import feature services.
|
|
5. Repositories must not call external providers.
|
|
6. Features must not read another feature's tables directly; cross-feature work goes through
|
|
an application service or declared read model.
|
|
7. Background jobs must call the same services used by HTTP flows rather than duplicate
|
|
calculations.
|
|
|
|
## 5. Feature Ownership
|
|
|
|
Each feature owns its routes, application service, schemas, repository interface, page module,
|
|
tests, and documentation. Initial feature IDs are defined in `features.config.json` during
|
|
Stage 04.
|
|
|
|
Shared code is allowed only when at least two features use the same stable behavior. A shared
|
|
module must not branch on page names or feature IDs to emulate unrelated components.
|
|
|
|
## 6. HTTP Contract
|
|
|
|
- Public application APIs use `/api/<resource>` and UTF-8 JSON.
|
|
- Controllers validate transport input, authorize the request, call one application service,
|
|
and serialize the result.
|
|
- Dates exposed to users use `YYYY-MM-DD`; provider-specific compact dates stay inside
|
|
adapters.
|
|
- Datetimes include an explicit timezone. Market time is interpreted as Asia/Shanghai.
|
|
- Successful collections use `items`; pagination uses `page`, `page_size`, and `total` when
|
|
required.
|
|
- Errors use a stable `code`, a user-safe `message`, and a request correlation ID. Provider
|
|
credentials and raw response bodies never appear in browser errors.
|
|
- Existing response shapes remain compatible until a versioned migration is approved.
|
|
- Backend authorization is authoritative. Hidden frontend controls are not a security rule.
|
|
|
|
## 7. Data Contract
|
|
|
|
Every calculation field declares:
|
|
|
|
- canonical field ID and Chinese display label;
|
|
- entity and frequency;
|
|
- type, unit, precision, timezone, and adjustment mode;
|
|
- unique primary source and permitted fallback sources;
|
|
- display-only or calculation-eligible status;
|
|
- freshness and completeness thresholds;
|
|
- missing-value behavior;
|
|
- point-in-time availability rules;
|
|
- owning dataset and persistence location.
|
|
|
|
Missing calculation data fails closed. A strategy may not silently replace a required field
|
|
with a proxy and continue under the original strategy name. Display fallbacks cannot enter
|
|
screening, scoring, backtesting, sentiment, or Wentian calculations unless explicitly approved
|
|
for that canonical field.
|
|
|
|
One OHLC bar or factor history must not mix providers. Source changes are recorded with the
|
|
stored observation and invalidate incompatible cached calculations.
|
|
|
|
## 8. Market Provider Policy
|
|
|
|
- Tushare is the default deterministic source for master data, calendar, daily bars,
|
|
fundamentals, valuations, industry data, lists, and post-close datasets.
|
|
- iFinD is the default source for licensed realtime snapshots, intraday data, dynamic auction
|
|
observations, charts, and approved event enrichment.
|
|
- Eastmoney and Tencent public endpoints are display or observation fallbacks only unless a
|
|
field contract explicitly promotes them.
|
|
- All provider traffic passes through `DataGateway`; provider classes only translate their own
|
|
protocol into canonical contracts.
|
|
- Retries, timeouts, quotas, cache TTL, circuit state, and provenance are centralized.
|
|
|
|
## 9. Persistence Standard
|
|
|
|
- SQLite WAL remains the current deployment database.
|
|
- Schema changes use ordered migration files with an immutable version ID.
|
|
- A migration is transactional where SQLite permits it and must be safe to run once.
|
|
- Destructive migrations require a verified backup and explicit acceptance.
|
|
- User-owned tables include `user_id`, a foreign key, an ownership index, and cross-account
|
|
tests.
|
|
- Repositories return domain-shaped records; controllers never execute SQL.
|
|
- Financial and research data preserve announcement timestamps to prevent look-ahead use.
|
|
- A future PostgreSQL adapter must satisfy the same repository contracts.
|
|
|
|
## 10. Background Jobs
|
|
|
|
- Jobs declare an ID, schedule, input date policy, dependencies, lock key, timeout, retry
|
|
policy, and idempotency key.
|
|
- Job runs persist start, completion, failure code, retry count, source coverage, and output
|
|
version.
|
|
- A process restart must not duplicate a completed post-close calculation.
|
|
- Exceptions are logged and surfaced in system management; they are never silently discarded.
|
|
- The current in-process runner may remain, but jobs cannot depend on thread-local request
|
|
context. This permits later extraction to a worker without changing business services.
|
|
|
|
## 11. LLM Standard
|
|
|
|
All model calls pass through `LLMGateway`, which owns:
|
|
|
|
- membership authorization and feature availability;
|
|
- quota reservation, settlement, and daily limits;
|
|
- model-pool selection and fallback;
|
|
- timeout, cancellation, retry, and streaming protocol;
|
|
- prompt version, model, latency, token usage, and failure audit;
|
|
- removal of secrets and engineering details from user-visible errors.
|
|
|
|
Skill evidence, source material, and data profiles remain separate from reusable prompt
|
|
templates. Deterministic calculations happen before the LLM call and are not delegated to the
|
|
model.
|
|
|
|
## 12. Frontend Standard
|
|
|
|
- `shared/api.js` is the only browser request exit.
|
|
- `shared/shell.js` owns sidebar, topbar, market summary, status bar, global dialogs, and page
|
|
mounting.
|
|
- `pages.config.js` owns navigation metadata and page loading, not authorization truth.
|
|
- A page module fetches data, owns page-local state, and composes shared components.
|
|
- Global mutable page state and cross-page DOM queries are prohibited after migration.
|
|
- Tables use shared shells with page-owned column schemas and formatters.
|
|
- Opening, closing, focus management, and feedback for dialogs use one dialog service.
|
|
|
|
## 13. CSS and Design Tokens
|
|
|
|
The cascade order is fixed:
|
|
|
|
```text
|
|
tokens -> reset/base -> shell/layout -> shared components -> page styles -> theme overrides
|
|
```
|
|
|
|
- Literal colors, font sizes, and spacing are introduced through tokens first.
|
|
- Page styles are scoped to the page root and cannot redefine the shell or another page.
|
|
- A later file cannot be used indefinitely as a patch layer for an earlier file.
|
|
- Shared components have one authoritative definition.
|
|
- CSS is removed only after selector/reference scanning, light/dark screenshots, and full
|
|
browser regression.
|
|
|
|
## 14. Mobile Standard
|
|
|
|
Mobile shares tokens, data, permissions, components, and state with desktop, but it may use a
|
|
different composition and interaction model.
|
|
|
|
- Shared mobile shell rules live in `shared/mobile-shell.css`.
|
|
- Each complex page may own `mobile.css` and a mobile view renderer.
|
|
- One global all-page `mobile.css` override pile is prohibited.
|
|
- Desktop tables may become summary lists and detail views on mobile.
|
|
- Mobile acceptance is performed independently at 390x844 and 430x932.
|
|
- Mobile changes must not alter approved desktop geometry.
|
|
|
|
## 15. Configuration and Secrets
|
|
|
|
- Environment variables seed deployment configuration; encrypted system settings hold managed
|
|
runtime credentials.
|
|
- Tokens, API keys, refresh tokens, model secrets, and encryption keys never enter Git, logs,
|
|
API responses, or browser storage.
|
|
- Feature flags, page metadata, field contracts, and permissions are versioned configuration,
|
|
not ad hoc conditionals.
|
|
|
|
## 16. Observability
|
|
|
|
External calls record provider, operation, elapsed time, cache result, freshness, normalized
|
|
error code, and correlation ID. Logs must not contain secrets or personal birth data.
|
|
|
|
Health checks distinguish process health, database health, provider degradation, background
|
|
job health, and model availability. Provider degradation does not make the process health
|
|
endpoint fail unless the application itself cannot serve stored data.
|
|
|
|
## 17. Change and Release Contract
|
|
|
|
Every governance phase must:
|
|
|
|
1. begin from a clean Git worktree;
|
|
2. preserve the Stage 01 baseline contract;
|
|
3. include migration or compatibility tests for changed boundaries;
|
|
4. pass `python tools/verify_baseline.py`;
|
|
5. run Playwright at phase boundaries affecting runtime or frontend behavior;
|
|
6. document residual risk;
|
|
7. create and push a dedicated rollback commit.
|
|
|
|
No phase combines framework replacement, visual redesign, and business-rule changes.
|
|
|
|
## 18. Public-Internet Evolution
|
|
|
|
The modular monolith remains the default. Reverse proxy/TLS, PostgreSQL, Redis, durable jobs,
|
|
central secrets, monitoring, and rate limiting can replace infrastructure adapters later.
|
|
Feature services and frontend contracts must not depend on whether those adapters are local or
|
|
remote. A feature is extracted into a separate service only when measured load, independent
|
|
release needs, or fault isolation justifies the operational cost.
|