Application Architecture¶
This chapter describes how to structure applications built with DSM and Viper. The architecture separates concerns into layers, enabling code reuse across platforms and languages.
The 3-Tier Architecture¶
Applications built with Viper follow a 3-tier pattern:
┌────────────────────────────────────────────────────────────┐
│ TIER 1: PRESENTATION (Platform-Specific UI) │
│ AppKit (macOS) / Qt (cross-platform) / PySide (Python) │
└────────────────────────────┬───────────────────────────────┘
│
┌────────────────────────────▼───────────────────────────────┐
│ TIER 2: LOGIC (Bridges + Store + Components) │
│ Generated pools, hand-written business code │
└────────────────────────────┬───────────────────────────────┘
│
┌────────────────────────────▼───────────────────────────────┐
│ TIER 3: DATA (Viper Runtime) │
│ CommitDatabase, persistence, dsviper binding │
└────────────────────────────────────────────────────────────┘
Tier Responsibilities¶
Tier |
Responsibility |
Platform-Specific? |
|---|---|---|
Presentation |
UI framework, windows, dialogs |
Yes |
Logic |
Bridges, Store, business components |
Partially |
Data |
Viper runtime, persistence |
No (shared) |
The Full Application Stack¶
For business applications with domain logic, the stack expands:
┌────────────────────────────────────────────────────────────┐
│ 1. UI Layer │
│ Platform-specific windows, views, controllers │
└────────────────────────────┬───────────────────────────────┘
│
┌────────────────────────────▼───────────────────────────────┐
│ 2. Store Layer │
│ Application store + generated pools │
└────────────────────────────┬───────────────────────────────┘
│
┌────────────────────────────▼───────────────────────────────┐
│ 3. Bridge Layer │
│ Pool signatures → Business logic connection │
└────────────────────────────┬───────────────────────────────┘
│
┌────────────────────────────▼───────────────────────────────┐
│ 4. Business Logic Layer │
│ Hand-written domain code (Model_*) │
└────────────────────────────┬───────────────────────────────┘
│
┌────────────────────────────▼───────────────────────────────┐
│ 5. Generated Infrastructure │
│ Kibo output: Data, Commit, Serialization │
└────────────────────────────┬───────────────────────────────┘
│
┌────────────────────────────▼───────────────────────────────┐
│ 6. Viper Runtime │
│ Core C++ engine, dsviper Python binding │
└────────────────────────────────────────────────────────────┘
Layer Details¶
Layer 1: UI¶
Platform-specific presentation code:
Window management
Menu and toolbar actions
Dialog boxes
Framework-native widgets
This layer changes for each target platform but follows the same logical structure.
Layer 2: Store¶
The Store orchestrates the application:
Holds the database reference
Manages application state
Provides undo/redo
Dispatches notifications to UI
// Store pattern (C++)
class Store {
public:
void createGraph(string label);
void newVertex(Position position);
void deleteSelection();
CommitDatabase* database();
bool canUndo();
void undo();
};
Layer 3: Bridges¶
Bridges connect generated pool signatures to your business logic:
// GE_AttachmentFunctionPoolBridges.hpp (GENERATED)
namespace GE::AttachmentFunctionPoolBridges {
namespace ModelGraph {
Graph::VertexKey new_vertex(std::shared_ptr<Viper::AttachmentMutating> const & attachmentMutating,
Graph::GraphKey const & graphKey,
std::int64_t value,
Graph::Position const & position);
}
}
// GE_AttachmentFunctionPoolBridges.cpp (HAND-WRITTEN)
Graph::VertexKey new_vertex(std::shared_ptr<Viper::AttachmentMutating> const & attachmentMutating,
Graph::GraphKey const & graphKey,
std::int64_t value,
Graph::Position const & position) {
return Model::Vertex::add(attachmentMutating, graphKey, value, position,
Model::Random::makeColor());
}
Layer 4: Business Logic¶
Your domain-specific code:
// GE_Model_Vertex.cpp
VertexKey add(std::shared_ptr<AttachmentMutating> const & attachmentMutating,
GraphKey const & graphKey,
std::int64_t const & value,
Position const & position,
Color const & color) {
auto const vertexKey{create(attachmentMutating, value, position, color)};
Attachments::Graph_Topology::unionVertexKeys(attachmentMutating, graphKey, {vertexKey});
return vertexKey;
}
Layer 5: Generated Infrastructure¶
Kibo generates this layer from your DSM:
Data types (
*_Data.cpp)Commit accessors (
*_Commit.cpp)Serialization (
*_Reader.cpp,*_Writer.cpp)Database persistence (
*_Database.cpp)Python proxy
Layer 6: Viper Runtime¶
The core C++ engine providing:
Type and value system
CommitDatabase persistence
Stateful commits
Python binding (dsviper)
The Notifier Pattern¶
UI frameworks have different notification mechanisms. The Notifier pattern bridges this gap:
CommitStore (C++ core)
│
▼
CommitStoreNotifying (interface)
│
▼
DSCommitStoreNotifier (framework adapter)
│
▼
Framework-specific notifications
Common Notifications¶
All implementations expose the same signals:
Signal |
Purpose |
|---|---|
|
Database opened successfully |
|
Database closed |
|
State changed (undo/redo available) |
|
Schema updated |
|
Error occurred |
Multi-Language Support¶
The architecture supports multiple languages:
Layer |
C++ Application |
Python Application |
|---|---|---|
UI |
AppKit/Qt C++ |
PySide/Qt Python |
Store |
C++ singleton |
dsviper |
Business |
C++ |
Python or dsviper |
Infrastructure |
C++ generated |
Python generated |
Runtime |
Viper C++ |
dsviper |
Python Integration¶
The dsviper module exposes the entire Viper runtime:
from dsviper import CommitStore, CommitDatabase
# Open database
database = CommitDatabase.open("path/to/data.cdb")
store = CommitStore.instance()
store.use(database)
# Undo/Redo operations
if store.can_undo():
store.undo()
Design Principles¶
Separation of Concerns¶
Each layer has a single responsibility:
UI: Only presentation, no business logic
Store: Orchestration, not implementation
Bridges: Connection, not logic
Business: Domain rules, not infrastructure
Generated: Infrastructure, not business
Platform Isolation¶
Platform-specific code lives only in Layer 1. The other layers are shared or generated.
Generated vs Hand-Written¶
Generated (Kibo) |
Hand-Written |
|---|---|
Data types |
Business logic |
Serialization |
Bridges |
Persistence |
Store orchestration |
Pool marshalling |
UI layer |
Minimal Porting Effort¶
To port to a new platform:
Implement the Notifier adapter (~50 lines)
Create UI components matching existing patterns
Wire up the main window
The business logic and infrastructure remain unchanged.
Summary¶
Principle |
Implementation |
|---|---|
Separation |
6 distinct layers with clear responsibilities |
Reuse |
Layers 3-6 shared across platforms |
Generation |
Kibo generates infrastructure (Layer 5) |
Multi-language |
Same architecture for C++ and Python |
Platform isolation |
Only Layer 1 is platform-specific |
This architecture enables building complex applications that work across platforms while minimizing duplicated code.
What’s Next¶
Python Guide - Use the dsviper Python API
Toolchain - Master the development tools