---
title: "Graph Database Architecture"
description: "Understanding TracerTM's graph-based traceability model and relationship patterns"
---

# Graph Database Architecture

TracerTM implements a **hybrid architecture** that combines PostgreSQL's relational model with application-layer graph algorithms to provide powerful traceability capabilities. While the data is stored in PostgreSQL, the system treats items and links as a directed graph for analysis purposes.

## Overview

TracerTM's graph model enables:

- **Traceability Analysis**: Track how requirements flow through implementation and testing
- **Impact Analysis**: Understand downstream effects of changes
- **Dependency Management**: Detect cycles and missing dependencies
- **Path Finding**: Calculate shortest paths and critical paths
- **Coverage Analysis**: Measure traceability coverage across views

### Why Hybrid Architecture?

TracerTM uses PostgreSQL as the primary data store with graph operations performed in the application layer:

**Benefits**:
- PostgreSQL provides ACID guarantees for transactional integrity
- Simpler deployment (single database system)
- Excellent query performance with proper indexing
- Graph algorithms can be optimized for specific use cases
- No additional database infrastructure required

**Trade-offs**:
- Graph queries performed in application memory
- Large graphs may require optimization
- No native graph query language (Cypher)

---

## Node Types

In the graph model, **Items** are nodes and **Links** are edges. The system supports flexible item types across different views.

### Item (Node) Model

```python
class Item:
    id: str              # UUID primary key
    project_id: str      # Project reference
    title: str           # Display name
    description: str     # Detailed description
    view: str            # Item classification (requirements, features, tests, etc.)
    item_type: str       # Type within view (functional, non-functional, unit, etc.)
    status: str          # Lifecycle state (todo, in_progress, done, etc.)
    priority: str        # Priority level (critical, high, medium, low)
    owner: str           # Assigned owner
    parent_id: str       # Hierarchical parent (optional)
    metadata: dict       # Flexible JSON storage
    version: int         # Optimistic locking version
    deleted_at: datetime # Soft delete timestamp
    created_at: datetime
    updated_at: datetime
```

### Common Views (Node Categories)

| View | Purpose | Example Item Types |
|------|---------|-------------------|
| `requirements` | System requirements | `functional`, `non-functional`, `constraint` |
| `features` | Product features | `epic`, `story`, `task` |
| `code` | Implementation units | `module`, `class`, `function` |
| `tests` | Test artifacts | `unit`, `integration`, `e2e`, `manual` |
| `api` | API definitions | `endpoint`, `schema`, `webhook` |
| `docs` | Documentation | `guide`, `reference`, `tutorial` |

### Node Properties

**Core Properties**:
- `id`: Unique identifier for graph traversal
- `view`: Primary node classification for filtering
- `item_type`: Secondary classification within view
- `status`: Current lifecycle state
- `priority`: Importance ranking

**Graph-Relevant Properties**:
- `parent_id`: Creates hierarchical relationships (separate from traceability links)
- `metadata`: Stores view-specific attributes (test results, API schemas, etc.)

---

## Relationship Types

Links represent directed relationships between items. The system supports typed relationships with metadata.

### Link (Edge) Model

```python
class Link:
    id: str              # UUID primary key
    project_id: str      # Project reference
    source_item_id: str  # Source node
    target_item_id: str  # Target node
    link_type: str       # Relationship type
    metadata: dict       # Flexible JSON storage
    created_at: datetime
    updated_at: datetime
```

### Standard Link Types

| Link Type | Semantics | Example Usage | Cycle Check |
|-----------|-----------|---------------|-------------|
| `implements` | Implementation relationship | Requirement → Feature → Code | No |
| `tests` | Testing relationship | Test → Code/Feature/Requirement | No |
| `depends_on` | Dependency relationship | Feature A → Feature B | **Yes** |
| `related_to` | General association | Documentation → Feature | No |

### Link Type Semantics

#### `implements`
**Direction**: Requirement → Feature → Code

Represents implementation flow from abstract to concrete:

```
[Requirement REQ-001] --implements--> [Feature FEAT-100]
[Feature FEAT-100] --implements--> [Code Module auth.py]
```

**Use Cases**:
- Forward traceability (requirements to implementation)
- Coverage analysis
- Verification tracking

#### `tests`
**Direction**: Test → Target

Represents testing relationships:

```
[Unit Test test_auth.py] --tests--> [Code Module auth.py]
[Integration Test api_test.py] --tests--> [API Endpoint /login]
[Manual Test MAN-005] --tests--> [Requirement REQ-001]
```

**Use Cases**:
- Test coverage analysis
- Quality assurance tracking
- Validation verification

#### `depends_on`
**Direction**: Dependent → Dependency

Represents dependency relationships with **cycle detection**:

```
[Feature FEAT-101] --depends_on--> [Feature FEAT-100]
```

**Special Properties**:
- **Cycle detection**: System prevents circular dependencies
- **Critical path**: Used for project scheduling
- **Impact analysis**: Changes propagate along dependency chains

**Constraints**:
```python
# Before creating depends_on link:
if cycle_service.has_cycle(project_id, source_id, target_id, "depends_on"):
    raise ValueError("Cannot create depends_on link: would create cycle")
```

#### `related_to`
**Direction**: Any → Any

General association without specific semantics:

```
[Documentation DOC-010] --related_to--> [Feature FEAT-100]
[API Endpoint /users] --related_to--> [API Schema UserSchema]
```

**Use Cases**:
- Cross-reference documentation
- Associate related artifacts
- Flexible linking without constraints

### Link Metadata

Links support flexible metadata for additional context:

```python
{
    "strength": 0.85,           # Link confidence (0.0 - 1.0)
    "verified": true,           # Manual verification flag
    "auto_generated": true,     # Created by auto-linking
    "source": "git_commit",     # Link creation source
    "rationale": "..."          # Explanation
}
```

---

## Graph Traversal Patterns

TracerTM implements several graph algorithms optimized for traceability analysis.

### 1. Impact Analysis (BFS)

**Algorithm**: Breadth-First Search (BFS)
**Complexity**: O(V + E)
**Purpose**: Find all items affected by a change

```python
# Downstream impact (forward direction)
result = await impact_service.analyze_impact(
    item_id="FEAT-100",
    max_depth=10,
    link_types=["implements", "tests"]
)

# Upstream impact (reverse direction)
result = await impact_service.analyze_reverse_impact(
    item_id="CODE-500",
    max_depth=10
)
```

**Returns**:
```python
{
    "root_item_id": "FEAT-100",
    "total_affected": 45,
    "max_depth_reached": 4,
    "affected_by_depth": {1: 5, 2: 15, 3: 20, 4: 5},
    "affected_by_view": {"code": 20, "tests": 15, "docs": 10},
    "affected_items": [...],
    "critical_paths": [...]  # Paths to leaf nodes
}
```

**Use Cases**:
- Change impact assessment
- Risk analysis
- Test planning

### 2. Shortest Path (Dijkstra)

**Algorithm**: Dijkstra's Algorithm
**Complexity**: O((V + E) log V)
**Purpose**: Find shortest path between two items

```python
path = await shortest_path_service.find_shortest_path(
    project_id="proj-1",
    source_id="REQ-001",
    target_id="TEST-500",
    link_types=["implements", "tests"]
)
```

**Returns**:
```python
{
    "source_id": "REQ-001",
    "target_id": "TEST-500",
    "path": ["REQ-001", "FEAT-100", "CODE-200", "TEST-500"],
    "distance": 3,
    "link_types": ["implements", "implements", "tests"],
    "exists": true
}
```

**Use Cases**:
- Traceability verification
- Gap analysis
- Requirements validation

### 3. Cycle Detection (DFS)

**Algorithm**: Depth-First Search (DFS)
**Complexity**: O(V + E)
**Purpose**: Detect circular dependencies

```python
# Check before creating link
has_cycle = cycle_service.has_cycle(
    project_id="proj-1",
    source_id="FEAT-101",
    target_id="FEAT-100",
    link_type="depends_on"
)

# Find all cycles in project
result = await cycle_service.detect_cycles_async(
    project_id="proj-1",
    link_types=["depends_on"]
)
```

**Returns**:
```python
{
    "has_cycles": true,
    "total_cycles": 2,
    "cycles": [
        ["FEAT-100", "FEAT-101", "FEAT-102", "FEAT-100"],
        ["FEAT-200", "FEAT-201", "FEAT-200"]
    ]
}
```

**Constraints**:
- Only enforced for `depends_on` link type
- Prevents creation of cyclic dependencies
- Enables critical path calculation

### 4. Critical Path (Topological Sort + DP)

**Algorithm**: Kahn's Algorithm + Dynamic Programming
**Complexity**: O(V + E)
**Purpose**: Calculate project critical path for scheduling

```python
result = await critical_path_service.calculate_critical_path(
    project_id="proj-1",
    link_types=["depends_on"]
)
```

**Returns**:
```python
{
    "project_id": "proj-1",
    "critical_path": ["FEAT-100", "FEAT-101", "FEAT-102"],
    "path_length": 3,
    "total_duration": 10,
    "critical_items": {"FEAT-100", "FEAT-101", "FEAT-102", "FEAT-200"},
    "slack_times": {
        "FEAT-100": 0,    # On critical path
        "FEAT-101": 0,    # On critical path
        "FEAT-103": 2     # Has 2 units of slack
    }
}
```

**Use Cases**:
- Project scheduling
- Resource optimization
- Risk identification

### 5. Traceability Matrix

**Algorithm**: Graph Join
**Complexity**: O(V × E)
**Purpose**: Generate coverage matrix between views

```python
matrix = await traceability_service.generate_matrix(
    project_id="proj-1",
    source_view="requirements",
    target_view="tests"
)
```

**Returns**:
```python
{
    "source_view": "requirements",
    "target_view": "tests",
    "links": [
        {
            "source_id": "REQ-001",
            "source_title": "User Authentication",
            "target_id": "TEST-100",
            "target_title": "Auth Integration Test",
            "link_type": "tests"
        }
    ],
    "coverage_percentage": 85.5,
    "gaps": [
        {"id": "REQ-010", "title": "Password Reset"}
    ]
}
```

**Use Cases**:
- Coverage reporting
- Gap analysis
- Compliance verification

---

## Query Examples

### Cypher-Style Patterns (Application Layer)

While TracerTM doesn't use Cypher, here are equivalent patterns:

#### Find All Tests for a Requirement

**SQL + Application Logic**:
```python
# Get requirement
requirement = await items.get_by_id("REQ-001")

# Get implementing features
impl_links = await links.get_by_source("REQ-001")
feature_ids = [l.target_item_id for l in impl_links if l.link_type == "implements"]

# Get tests for those features
test_links = []
for fid in feature_ids:
    links = await links.get_by_target(fid)
    test_links.extend([l for l in links if l.link_type == "tests"])
```

**Cypher Equivalent** (for reference):
```cypher
MATCH (req:Item {id: 'REQ-001'})
      -[:IMPLEMENTS]->(feat:Item)
      <-[:TESTS]-(test:Item)
WHERE feat.view = 'features' AND test.view = 'tests'
RETURN test
```

#### Find Upstream Dependencies

**Application Logic**:
```python
result = await impact_service.analyze_reverse_impact(
    item_id="FEAT-100",
    max_depth=5
)
```

**Cypher Equivalent**:
```cypher
MATCH path = (item:Item {id: 'FEAT-100'})
             <-[:DEPENDS_ON*1..5]-(dependent:Item)
RETURN dependent, length(path) as depth
```

#### Detect Missing Test Coverage

**Application Logic**:
```python
# Get all code items
code_items = await items.get_by_view(project_id, "code")

# Check which have no test links
untested = []
for item in code_items:
    test_links = await links.get_by_target(item.id)
    has_test = any(l.link_type == "tests" for l in test_links)
    if not has_test:
        untested.append(item)
```

**Cypher Equivalent**:
```cypher
MATCH (code:Item {view: 'code'})
WHERE NOT (code)<-[:TESTS]-()
RETURN code
```

---

## Performance Optimization

### Indexing Strategy

Critical indexes for graph operations:

```sql
-- Item lookups by project and view
CREATE INDEX idx_items_project_view ON items(project_id, view);

-- Link traversal
CREATE INDEX idx_links_source_target ON links(source_item_id, target_item_id);
CREATE INDEX idx_links_project_type ON links(project_id, link_type);

-- Status filtering
CREATE INDEX idx_items_project_status ON items(project_id, status);
```

### Caching Strategy

For frequently accessed graph data:

```python
# Cache adjacency lists for large projects
@lru_cache(maxsize=100)
async def get_adjacency_list(project_id: str) -> dict[str, list[str]]:
    links = await link_repo.get_by_project(project_id)
    adj = {}
    for link in links:
        if link.source_item_id not in adj:
            adj[link.source_item_id] = []
        adj[link.source_item_id].append(link.target_item_id)
    return adj
```

### Optimization Tips

1. **Batch Loading**: Load all items/links for a project into memory for graph operations
2. **Depth Limiting**: Always specify `max_depth` to prevent runaway queries
3. **Link Type Filtering**: Filter by `link_type` early to reduce graph size
4. **Soft Delete Handling**: Exclude `deleted_at IS NOT NULL` items early
5. **Pagination**: For large result sets, use offset/limit on affected items

### Performance Benchmarks

Expected performance on modern hardware:

| Operation | Items | Links | Time |
|-----------|-------|-------|------|
| Impact Analysis (depth=5) | 1,000 | 5,000 | ~50ms |
| Shortest Path | 1,000 | 5,000 | ~30ms |
| Cycle Detection | 1,000 | 5,000 | ~100ms |
| Critical Path | 1,000 | 5,000 | ~150ms |
| Traceability Matrix | 500 × 500 | 2,000 | ~200ms |

**Scaling Considerations**:
- Graphs with 10,000+ items may benefit from incremental analysis
- Consider materialized views for frequently accessed matrices
- Use background jobs for large-scale analysis

---

## When to Use Graph vs Relational Queries

### Use Graph Operations When:

✅ **Traversing Relationships**
- Finding all downstream dependencies
- Impact analysis across multiple hops
- Path finding between items

✅ **Analyzing Connectivity**
- Detecting cycles
- Finding critical paths
- Measuring graph density

✅ **Coverage Analysis**
- Traceability matrix generation
- Gap identification
- Coverage percentage calculation

### Use Relational Queries When:

✅ **Direct Lookups**
- Getting item by ID
- Listing items in a view
- Filtering by status/priority

✅ **Aggregations**
- Counting items by type
- Grouping by view
- Status distributions

✅ **Single-Hop Relationships**
- Direct links from/to an item
- Parent-child hierarchies
- Simple joins

### Example Decision Matrix

| Use Case | Approach | Reason |
|----------|----------|--------|
| "Show all tests for this feature" | **Relational** (1 hop) | Direct join on links table |
| "What features depend on this?" | **Graph** (BFS) | Multi-hop traversal needed |
| "List all requirements" | **Relational** | Simple filter query |
| "Calculate test coverage path" | **Graph** (Path finding) | Multi-hop relationship |
| "Count items by status" | **Relational** | Aggregation query |
| "Find circular dependencies" | **Graph** (DFS) | Cycle detection algorithm |

---

## Best Practices

### 1. Link Type Design

**Do**:
- Use semantic link types (`implements`, `tests`, `depends_on`)
- Document link direction and meaning
- Enforce constraints (e.g., cycles) where appropriate

**Don't**:
- Create too many link types (causes confusion)
- Use bidirectional link types (always use source → target)
- Mix hierarchy (parent_id) with traceability (links)

### 2. Graph Traversal

**Do**:
- Always specify `max_depth` to prevent infinite loops
- Filter by `link_types` to reduce graph size
- Use BFS for shortest path/impact analysis
- Use DFS for cycle detection

**Don't**:
- Traverse entire graph without depth limit
- Ignore soft-deleted items in traversal
- Perform graph operations on every request (cache when possible)

### 3. Data Modeling

**Do**:
- Use `view` for primary classification
- Use `item_type` for secondary classification
- Store view-specific data in `metadata`
- Use `parent_id` for hierarchies within a view

**Don't**:
- Mix different concerns in same view
- Store large blobs in metadata (use separate tables)
- Create deep hierarchies (limit to 3-4 levels)

### 4. Performance

**Do**:
- Load graph data into memory for analysis
- Use database indexes on foreign keys
- Batch load related items
- Cache adjacency lists for large projects

**Don't**:
- Perform graph traversal in database with recursive CTEs
- Make N+1 queries (load related data in batches)
- Ignore query complexity in large graphs

---

## Migration Considerations

### From Pure Relational to Graph-Aware

If you have existing relational data:

1. **Add Link Types**: Standardize on 4 core types
2. **Create Indexes**: Add graph traversal indexes
3. **Migrate Relationships**: Convert foreign keys to links where appropriate
4. **Enable Cycle Detection**: Add constraint to `depends_on` links

### Future: Native Graph Database

If the project grows to 100,000+ items, consider:

1. **Neo4j Integration**:
   - Use Neo4j for graph operations
   - Keep PostgreSQL for transactional data
   - Sync graph to Neo4j via change data capture

2. **Benefits**:
   - Native Cypher queries
   - Better performance for complex traversals
   - Built-in graph algorithms

3. **Migration Path**:
   ```python
   # PostgreSQL → Neo4j sync
   for item in items:
       neo4j.create_node("Item", **item.dict())

   for link in links:
       neo4j.create_relationship(
           link.source_item_id,
           link.link_type.upper(),
           link.target_item_id
       )
   ```

---

## Related Documentation

- [PostgreSQL Schema](/docs/03-development/01-architecture/04a-postgres-schema) - Relational data model
- [Database Design](/docs/03-development/01-architecture/03-database-schema) - Overall database architecture
- [API Design](/docs/03-development/01-architecture/04-api-design) - REST endpoints for graph operations

## Next Steps

- Implement graph-based features in your application
- Optimize queries for large datasets
- Monitor graph operation performance
- Consider Neo4j for scale
