# Repository Pattern for Data Access in Fincept Terminal: Architecture and Implementation

> Learn how Fincept Terminal leverages the repository pattern for data access. Explore its architecture, thread-safe SQLite wrapper, and type-safe APIs for efficient storage management.

- Repository: [Fincept Corporation/FinceptTerminal](https://github.com/Fincept-Corporation/FinceptTerminal)
- Tags: architecture
- Published: 2026-04-20

---

**Fincept Terminal implements a generic repository pattern built on a thread-safe SQLite wrapper, combining a templated `BaseRepository` class for shared CRUD operations with singleton concrete repositories that expose type-safe, domain-specific APIs.**

The storage layer in [FinceptTerminal](https://github.com/Fincept-Corporation/FinceptTerminal) provides a robust data access architecture designed for maintainability and type safety in a Qt-based environment. At its core lies a repository pattern implementation that separates low-level SQL execution from business logic through a sophisticated template-based inheritance hierarchy. This design enables consistent SQLite interactions across the financial terminal while keeping entity-specific code clean and testable.

## Core Components of the Storage Layer

Fincept’s data access stack consists of three coordinated layers that work together to provide type-safe database operations.

### The Database Singleton Wrapper

At the foundation sits the **`Database`** singleton located in [`fincept-qt/src/storage/sqlite/Database.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/storage/sqlite/Database.h). This class provides a thread-safe entry point through `Database::instance()` that manages the SQLite connection lifecycle, executes raw statements, and handles transaction boundaries. The public API exposes methods like `execute`, `exec`, and `begin_transaction`, which every repository in the system utilizes for actual database communication. By encapsulating SQLite details within this single utility, the architecture ensures that connection pooling and error handling remain centralized.

### The BaseRepository Template

Building atop the Database wrapper is the header-only **`BaseRepository<Entity>`** template found in [`fincept-qt/src/storage/repositories/BaseRepository.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/storage/repositories/BaseRepository.h). This generic base class supplies common CRUD helpers including `query_list_as`, `query_list`, `query_one`, `query_optional`, `exec_write`, `exec_insert`, and `exec_raw`. Because it is templated on the entity type, each concrete repository inherits these low-level operations without code duplication. The implementation also integrates failure logging through the shared `Logger`, ensuring that database errors are captured consistently across the storage layer.

### Concrete Repository Implementations

Every table in the schema has a dedicated repository class that inherits from `BaseRepository<Entity>`. These concrete implementations follow a strict pattern:

- **Singleton Access**: Each exposes `static XRepository& instance()` to guarantee a single entry point per entity type.
- **Domain-Specific Operations**: Methods like `SettingsRepository::set`, `WatchlistRepository::add_stock`, and `InstrumentRepository::search` provide business-logic APIs while internally reusing `BaseRepository` helpers.
- **Row Mapping**: Static `map_row` functions convert `QSqlQuery` rows into C++ struct instances, enabling type-safe data retrieval.

## Implementing Domain-Specific Repositories

Concrete repositories extend the base template to provide strongly-typed interfaces for specific entities. The `SettingsRepository` in [`fincept-qt/src/storage/repositories/SettingsRepository.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/storage/repositories/SettingsRepository.h) and its corresponding `.cpp` implementation demonstrate this pattern clearly.

For write operations, repositories delegate to inherited helpers:

```cpp
// From fincept-qt/src/storage/repositories/SettingsRepository.cpp
Result<void> SettingsRepository::set(const QString& key,
                                     const QString& value,
                                     const QString& category) {
    // exec_write comes from BaseRepository<Setting>
    return exec_write(
        "INSERT OR REPLACE INTO settings (key, value, category) VALUES (?, ?, ?)",
        {key, value, category});
}

```

For queries that return collections, the repository passes a mapping function to the generic `query_list` method:

```cpp
// Generic query that returns typed results
Result<QVector<Setting>> SettingsRepository::get_by_category(const QString& cat) {
    // query_list is defined in BaseRepository
    return query_list(
        "SELECT key, value, category, updated_at FROM settings WHERE category = ? ORDER BY key",
        {cat},
        map_row);   // map_row converts QSqlQuery → Setting
}

```

The `map_row` function is defined as a static member that constructs entity structs from query results, ensuring the conversion logic lives alongside the repository but remains reusable by the base class templates.

## Working with Entity Structs

Entity objects are defined as plain-old-data structs (e.g., `Setting`, `Watchlist`, `Instrument`) that live adjacent to their repositories. These structs contain fields corresponding directly to database columns, with no inheritance requirements or complex constructors. The repository’s `map_row` function serves as the bridge between the relational database and these C++ objects, maintaining a clean separation between storage representation and application logic.

## Practical Usage Examples

Client code interacts with the storage layer exclusively through concrete repository singletons, never touching SQL directly:

```cpp
// Retrieve a setting with default fallback using SettingsRepository
auto val = fincept::SettingsRepository::instance()
               .get("theme", "light")
               .unwrap();               // Result<QString>
qDebug() << "Current theme:" << val;

// Complex entity creation with WatchlistRepository
auto w = fincept::WatchlistRepository::instance()
             .create("Tech", "#00AEEF")
             .unwrap();               // Result<Watchlist>

fincept::WatchlistRepository::instance()
    .add_stock(w.id, "AAPL", "Apple Inc.", "NASDAQ")
    .unwrap();

```

The `InstrumentRepository` in [`fincept-qt/src/trading/instruments/InstrumentRepository.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/trading/instruments/InstrumentRepository.h) demonstrates the pattern applied to high-frequency trading data, offering methods for instrument lookup, search, and bulk replace operations while relying on `BaseRepository` for actual database interaction.

## Summary

FinceptTerminal’s repository pattern implementation delivers a type-safe, reusable, and testable data access architecture:

- **Generic BaseRepository template** provides shared query and execution logic across all entity types.
- **Singleton concrete repositories** offer domain-specific APIs with guaranteed single-instance access.
- **Database wrapper** encapsulates SQLite specifics and transaction management in one location.
- **Static row mapping** connects SQL results to C++ structs without runtime overhead.

This structure ensures that data-access concerns remain isolated from business logic, making the codebase maintainable and the storage layer easily extensible for new financial entities.

## Frequently Asked Questions

### How does the BaseRepository template handle different entity types?

The `BaseRepository<Entity>` class uses C++ templates to accept any entity struct as its type parameter. This allows the base class to provide generic methods like `query_list` and `exec_write` while returning strongly-typed results specific to the entity. Each concrete repository inherits from `BaseRepository<TheirEntityType>`, gaining access to shared database logic without sacrificing type safety.

### Why are concrete repositories implemented as singletons?

Concrete repositories use the singleton pattern (`static XRepository& instance()`) to ensure a single, global access point for each entity's data operations. This eliminates the need to pass repository instances through constructors, simplifies the API for client code, and guarantees that all database operations for a specific entity route through the same configured instance that inherits the base class's connection management.

### Where is the SQL logic located in this architecture?

SQL statements reside within the concrete repository implementation files, such as [`SettingsRepository.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/SettingsRepository.cpp) or [`InstrumentRepository.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/InstrumentRepository.cpp). While string literals containing queries are defined in these specific repositories, the actual execution logic—parameter binding, error handling, and result iteration—is handled by the inherited methods from `BaseRepository` and the underlying `Database` singleton in [`fincept-qt/src/storage/sqlite/Database.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/storage/sqlite/Database.h).

### How does the repository handle data mapping from SQLite to C++ objects?

The pattern uses static `map_row` functions defined in each concrete repository. These functions accept a `QSqlQuery` reference and return a populated entity struct. When calling base methods like `query_list` or `query_one`, the repository passes `map_row` as a callback pointer, enabling the generic base class to perform the iteration while the repository-specific code handles the column-to-field mapping.