| name | typetype-architecture |
| description | Understand TypeType project architecture, layering rules, dependency directions, and component responsibilities. Use when working on TypeType, adding features, or refactoring. |
TypeType Architecture
Overview
TypeType = PySide6/QML typing practice app. Clean Architecture + Ports & Adapters.
Dependency direction (MUST follow):
QML UI โ Presentation โ Application โ Domain/Ports โ Integration/Infrastructure
Directory Structure
src/backend/
โโโ application/
โ โโโ exception_handler.py # Global exception โ user message mapping
โ โโโ gateways/
โ โ โโโ score_gateway.py # DTO conversion + clipboard
โ โ โโโ text_source_gateway.py # Text source routing + Port adapter
โ โโโ usecases/
โ โโโ load_text_usecase.py # Text loading orchestration
โโโ ports/ # Protocol definitions (top-level)
โ โโโ async_executor.py
โ โโโ auth_provider.py
โ โโโ char_stats_repository.py
โ โโโ clipboard.py
โ โโโ local_text_loader.py
โ โโโ ranking_repository.py
โ โโโ text_provider.py
โโโ config/
โ โโโ runtime_config.py # Text sources configuration
โโโ domain/
โ โโโ services/ # Pure business logic (NO Qt dependency)
โ โโโ typing_service.py
โ โโโ auth_service.py
โ โโโ char_stats_service.py
โโโ models/ # Data models
โ โโโ entity/ # Domain entities (char_stat, session_stat)
โ โโโ dto/ # Data transfer objects
โโโ infrastructure/ # Infrastructure
โ โโโ api_client.py # HTTP client wrapper
โ โโโ network_errors.py # Network error classification
โโโ integration/ # Port implementations
โโโ presentation/
โ โโโ bridge.py # QML facade (appBridge)
โ โโโ adapters/ # Qt adaptation layer
โโโ security/ # Encryption + secure storage
โโโ utils/ # Logger
โโโ workers/ # Background tasks (QRunnable)
Layering Rules (MUST Follow)
โ
ALLOWED Dependencies
Bridge โ Adapters
Adapters โ Application (UseCases/Gateways)
Adapters โ Domain (pure business calls directly)
Application โ Domain / Ports / Config
Integration / Infrastructure โ Ports / Domain
โ FORBIDDEN Dependencies
Presentation โ Integration / Infrastructure # Breaks dependency inversion
Domain โ Qt / PySide / QML # Domain must be pure business
UseCases โ Qt types # UseCases must not depend on Qt
Adapter makes business routing decisions # Routing โ Application layer only
RuntimeConfig Rules
| Usage | Allowed |
|---|
| Gateway holds + makes routing decisions | โ
YES |
| Adapter shows for UI (source list, default) | โ
YES |
| Adapter makes business routing decisions | โ NO |
| Adapter decides sync/async execution strategy | โ NO |
Component Responsibilities
| Layer | Component | Responsibility |
|---|
| QML UI | src/qml/ | Display, user interaction |
| Presentation | Bridge | QML facade: property proxy, signal forwarding |
| Presentation | Adapters | Qt adaptation: thread coordination |
| Application | UseCases | Business process orchestration |
| Application | Gateways | Config query, Port adaptation, DTO conversion |
| Domain | Services | Pure business logic, NO Qt dependency |
| Ports | Protocols | Abstract dependency interface |
| Integration | Implementations | Qt/SQLite/HTTP concrete implementations |
Key Data Flow: Text Loading
QML โ Bridge โ TextAdapter โ LoadTextUseCase.plan_load() โ outputs sync/async
โ
TextAdapter executes or enqueues โ LoadTextUseCase.load()
โ
TextSourceGateway routes โ Local/Network โ result โ QML
When to Create What
| You want... | Create/Modify... |
|---|
| Add new UI feature | Bridge + Adapter |
| Add business process | UseCase (Application) |
| Add external dependency | Define Port + implement Integration |
| Add pure business rule | Domain Service |
| Add data persistence | Domain Service + Port + Integration |
Score Submission Architecture
Rule: Only server-managed texts can have scores.
- Score submission sends only
textId (server PK) โ no sourceKey, no content, no hash
- Server
ScoreService.findById(textId) โ record score. No findOrCreate.
- Local files and clipboard = practice only, no scores, no server interaction
- Texts enter the server via: admin API upload OR auto-fetch (e.g., SaiWen)
Data flow:
Leaderboard mode: GET /texts/latest/{sourceKey} โ Text(id=49) โ type โ POST /scores {textId: 49}
Practice mode: local file โ type โ done (no server call)
Why not auto-create texts on score submission:
- Local files differ between users โ would pollute DB with duplicate/inconsistent texts
- findOrCreate requires content hashing โ client/server hash algorithm must stay in sync
- Simpler to enforce: text must exist before score can reference it
Non-Negotiable Principles
- Clear dependency direction always
- UseCase only when needed - no pure-forwarding UseCases
- Adapter can call Domain directly - no forced UseCase
- Domain is pure business - NO Qt import EVER
RinUI Rules
RinUI/ = vendored third-party โ DO NOT MODIFY
pyproject.toml excludes RinUI from ruff โ keep
- QML colors:
Theme.currentTheme.colors.*
- Normal UI font follows the global app font set in
main.py; only dedicated reading/typing areas should set a custom fontFamily (current example: TypingPage.qml uses LXGW WenKai forๆญฃๆๅบ)
- Name conflict:
import RinUI as Rin
Verification Checklist โ