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