CinderPeak follows the Separation of Concerns principle and implements a layered architecture, ensuring that each component has a clearly defined responsibility. This applies to both the user-facing API and the storage engine (PeakStore). The design aims to make the system modular, extensible, and easy to maintain, while also enabling targeted optimizations.
The communication model consists of three layers, each with distinct roles, data flow constraints, and synchronized operations.
The three primary layers are:
Layer 1 — User API Layer (Facade)
Layer 2 — Internal Storage Orchestrator (PeakStore)
Layer 3 — Concrete Storage Backends
Each layer follows specific design patterns, as illustrated below:
Fig 1.1: Design Pattern of each layerCommunication between layers can be unidirectional or bidirectional, but with strict ordering: Layer 1 ↔ Layer 2 ↔ Layer 3.
Fig 1.1: Bidirectional layer communication flowDirect jumps between non-adjacent layers are not allowed. For example, Layer 1 cannot directly access Layer 3 — it must go through Layer 2.
Note: Layer 0 (not shown) is the user side code or the code accessing layer 1 from the user's end, which calls the Layer 1 APIs.
Layer 1 is the entry point for all user interactions. It exposes a clean, minimal, and intuitive public API for operations like:
-
Creating graphs
-
Adding/removing vertices and edges
-
Running graph algorithms
-
Querying graph properties
Responsibilities:
-
Translate high-level user calls into lower-level instructions.
-
Provide error handling and validation before delegating to deeper layers.
-
Keep the user isolated from internal complexities.
Design Pattern: Layer 1 follows the Facade Pattern, meaning it serves as a “single doorway” into the system. The internal complexity of graph storage and operations is completely hidden from the user.
Example in action:
When a user calls graph.addEdge(1, 2), Layer 1 will:
-
Validate inputs (e.g., check that both vertices exist).
-
Forward the request to Layer 2 for processing.
Layer 2 is the heart of CinderPeak, responsible for managing all storage backends in Layer 3.
It acts as a mediator between the API (Layer 1) and actual storage implementations (Layer 3).
Fig 1.2: Layer 2 internal storageKey responsibilities:
-
Instantiate, manage, and dispose storage backends.
-
Maintain a shared context object containing:
-
Storage flags (e.g., backend type, memory mode)
-
Active storage engine details
-
Graph-level configuration
-
Graph metadata (e.g., directed/undirected, weighted/unweighted), number of edges, vertices etc.
-
-
Translate generic graph operations into backend-specific calls.
Design Patterns Used:
-
Mediator Pattern — Simplifies communication between backends.
-
Dependency Injection — Passes the shared context into backends so they operate with consistent settings.
Example in action: When addEdge reaches Layer 2, it determines whether the active backend is Adjacency List, CSR, or COO, and delegates the insertion accordingly.
Layer 3 contains concrete implementations for different graph storage formats:
-
Adjacency List
-
Hybrid CSR (Compressed Sparse Row) - COO (Coordinate List)
Future formats can be added without touching higher layers.
Fig 1.3: Storage implementations with Strategy+Bridge patternsCore Principles:
-
All backends inherit from a common StorageInterface.
-
Each backend implements methods like:
-
addVertex
-
addEdge
-
removeEdge
-
getNeighbors
-
Design Patterns Used:
-
Strategy Pattern — Allows PeakStore to switch backends at runtime based on performance needs or user preference.
-
Bridge Pattern — Decouples the abstraction (PeakStore) from the implementation (storage engines).
Example in action: If the user switches from Adjacency List to CSR for performance reasons, Layer 2 simply updates the backend reference without needing to change Layer 1’s API or Layer 3’s logic.
CinderPeak uses Google Test (GTest) for a mix of unit tests and integration tests across all layers and backends.
The testing workflow is modular, split into five independent pipeline shards. This ensures that storage-specific failures don’t block unrelated tests and allows selective execution for faster CI/CD runs.
Pipeline Shards:
-
Shard 1: Tests Adjacency List backend
-
Shard 2: Tests CSR(Compressed Sparse Row) backend
-
Shard 3: Tests COO(Coordinate List) backend
-
Shard 4: Tests Graph Matrix representation
-
Shard 5: Tests Graph List representation
Execution Rules:
-
Shards 1–3 run in parallel for efficiency.
-
Shards 4–5 execute only if Shards 1–3 pass, ensuring no dependent failures.
All shards are managed by CinderFlow, the central test orchestrator, which:
-
Runs tests locally and in CI/CD environments (GitHub Actions, GitLab CI, etc.)
-
Supports selective shard execution
-
Reports consolidated results
Example CI/CD flow:
-
Developer pushes code.
-
Shards 1–3 run in parallel.
-
If all pass, Shards 4–5 execute.
-
CinderFlow aggregates results and reports pass/fail status.





