Fincept Terminal Screen Service Architecture Pattern: Complete Technical Guide
Fincept Terminal implements a strict Screen↔Service separation pattern built on Qt’s signal/slot mechanism, where UI screens in fincept-qt/src/screens/* handle purely presentational concerns while business logic resides in fincept-qt/src/services/*, communicating via the ScreenRouter and EventBus for modular, testable desktop architecture.
The Fincept-Corporation/FinceptTerminal repository demonstrates a sophisticated screen service architecture pattern that cleanly isolates user interface rendering from business logic using Qt’s framework capabilities. This architecture, documented in docs/ARCHITECTURE.md, enables the financial terminal to maintain responsive UIs while executing heavy computational tasks asynchronously. By preventing screens from directly accessing HttpClient, PythonRunner, or database layers, the codebase achieves clear module boundaries and comprehensive testability.
UI Layer: The Screen Abstraction
Screens reside in fincept-qt/src/screens/* and represent pure Qt widgets responsible solely for data presentation and user input capture. Each screen emits signals when user interactions occur but never executes business logic directly.
The MarketsScreen.cpp implementation exemplifies this pattern. According to the source code (lines 37‑44), screens connect to services exclusively through Qt signals and slots, delegating all data fetching to service layers. Screens never instantiate HTTP clients, execute Python scripts, or manage caching strategies.
This separation ensures that UI components remain lightweight and focused on rendering, while the complexity of external API calls and data processing remains encapsulated elsewhere.
Application Layer: Service Architecture
Services live in fincept-qt/src/services/* and encapsulate all non‑UI operations including network requests, script execution, and data persistence. The MarketDataService.cpp implementation (lines 59‑93) demonstrates how services handle batch request optimization, caching layers, and Python integration.
Services expose clean APIs that screens can invoke, such as MarketDataService::fetch_quotes(), which accepts a symbol list and callback. Internally, services utilize HttpClient, PythonRunner, SQLite, and QThread/QtConcurrent for asynchronous processing, then deliver results back to the UI thread via thread‑safe mechanisms.
This architecture creates a clear boundary where MarketsScreen calls MarketDataService::instance().fetch_quotes() without knowing whether data comes from cache, HTTP endpoints, or Python calculations.
Navigation and Lazy Loading
The ScreenRouter class (fincept-qt/src/app/ScreenRouter.h and fincept-qt/src/app/ScreenRouter.cpp) manages view transitions using a QStackedWidget container. It supports two registration strategies:
- Eager registration via
register_screen()constructs widgets immediately during application startup - Lazy registration via
register_factory()defers widget construction until first navigation, significantly reducing startup latency
Lazy factories prove essential for complex financial terminals where instantiating all screens upfront would degrade performance. The router handles lifecycle management while maintaining the separation between navigation logic and screen content.
Cross‑Layer Communication Mechanisms
Screens and services communicate through two primary channels. Direct signal/slot connections allow services to push updates to specific screens, as implemented in MarketsScreen.cpp lines 37‑44. Broadcast messaging occurs through EventBus::instance().publish(...) (fincept-qt/src/core/events/EventBus.cpp), enabling decoupled communication where multiple screens can react to service state changes without direct wiring.
Thread safety remains critical in this pattern. UI code runs exclusively on the main thread, while services offload heavy work to background threads. Results return to the UI via QMetaObject::invokeMethod or cross‑thread signal/slot connections, preventing blocking operations from freezing the interface.
Error Handling and Infrastructure
All modules employ the Result<T> wrapper defined in fincept-qt/src/core/result/Result.h for explicit error propagation. This eliminates exception‑based control flow in favor of typed returns that callers must handle explicitly.
Structured logging occurs through LOG_INFO and LOG_ERROR macros defined in core/logging/Logger.h, providing consistent observability across screens and services. Combined with the threading model—main thread for UI, QThread for services—this infrastructure supports robust financial data processing without compromising interface responsiveness.
Implementation Example
The following code illustrates the strict separation between UI screens and business services:
// fincept-qt/src/screens/markets/MarketsScreen.cpp – UI layer
void MarketsScreen::refresh_all() {
// Screen delegates entirely to service, receives callback
MarketDataService::instance().fetch_quotes({"AAPL", "MSFT"},
[this](bool ok, QVector<QuoteData> quotes) {
if (!ok) return;
for (const auto& q : quotes) {
update_panel(q.symbol, q.price, q.change_pct);
}
});
}
// fincept-qt/src/services/markets/MarketDataService.cpp – Service layer
void MarketDataService::fetch_quotes(const QStringList& symbols,
QuoteCallback cb) {
// Implementation handles caching, HTTP batching,
// Python script execution (lines 59‑93), then invokes
// callback on UI thread with results.
}
The screen never accesses HttpClient or PythonRunner; it invokes the service façade and receives data through the provided callback mechanism.
Summary
- Screen↔Service separation creates strict boundaries between
fincept-qt/src/screens/*(UI) andfincept-qt/src/services/*(business logic) - Qt signals/slots and
EventBusprovide decoupled communication channels without direct dependencies - ScreenRouter manages navigation with eager and lazy registration strategies to optimize startup performance
- Thread safety ensures UI responsiveness through
QThread/QtConcurrentin services andQMetaObject::invokeMethodfor results - Result wrappers and structured logging macros provide robust error handling across all layers
Frequently Asked Questions
How does Fincept Terminal handle thread safety between screens and services?
UI code runs exclusively on the main thread while services execute heavy operations in QThread or QtConcurrent workers. Services return results to screens using QMetaObject::invokeMethod or cross‑thread signal/slot connections, ensuring the UI never blocks during HTTP requests or Python script execution.
What is the difference between eager and lazy screen registration in ScreenRouter?
register_screen creates widget instances immediately during application initialization, suitable for frequently accessed views. register_factory stores factory functions that defer construction until first navigation, reducing startup time and memory footprint for complex terminal interfaces with many potential screens.
How does the EventBus differ from direct signal/slot connections?
Direct signal/slot connections in MarketsScreen.cpp (lines 37‑44) create point‑to‑point communication between specific screens and services. The EventBus (fincept-qt/src/core/events/EventBus.cpp) enables broadcast messaging where services publish events without knowing which screens subscribe, supporting one‑to‑many updates and decoupled module dependencies.
What error handling mechanism does Fincept Terminal use for service operations?
All service methods return Result<T> types defined in fincept-qt/src/core/result/Result.h rather than throwing exceptions. This explicit error handling pattern forces callers to check success/failure states, while LOG_INFO and LOG_ERROR macros in core/logging/Logger.h capture structured diagnostic information for debugging.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →