# Cross-Domain Linking Best Practices
**A Step-by-Step Guide for Connecting Domain Islands with Minimum Spanning Tree Approach**

## 🎯 Purpose

This guide provides a proven workflow for connecting multiple domain ontologies that have been internally linked but remain isolated "islands" in a knowledge graph. It creates cross-domain object property relationships using shared identifiers.

**When to Use**: When you have multiple well-linked domain ontologies (e.g., CRM, ERP, PLM, Manufacturing) but they lack connections between domains.

**Example**: PLM Parts have `productId` and MES Production Orders have `productId`, but no direct object property links exist between them.

---

## ✅ The Golden Rules (Adapted from Internal Linking)

### Rule #1: Foreign Keys and Shared Attributes First
**Internal Linking:** Look for explicit FKs within domain (e.g., `Order.customerId` → `Customer.id`)  
**Cross-Domain Adaptation:** Look for **shared identifiers across domains**:
- `productId` (may appear in PLM, Manufacturing, Quality, Regulatory, Service)
- `customerId` (may appear in CRM, Service, Finance)
- `equipmentId` (may appear in CRM, Service, Manufacturing)
- `supplierId` (may appear in Supply Chain, Quality, Finance)
- `batchNumber`/`lotNumber` (may appear in Manufacturing, Supply Chain, Quality)

**Action:** Catalog all shared identifiers and their coverage percentages across domain pairs.

### Rule #2: Validate with SELECT COUNT Before Creating Links
**Same as Internal:** Always test link queries with `SELECT (COUNT(*) as ?links)` before implementing.

**Cross-Domain Caution:** 
- Coverage may be lower than internal links (expect 20-60% vs 70-95%)
- Identifier naming may vary (e.g., `productId` vs `productNumber` vs `partNumber`)
- Data quality gaps more common across domain boundaries

**Action:** Accept lower coverage for Phase 1 if connection is semantically valid.

### Rule #3: Avoid Cartesian Products
**Same as Internal:** Validate that link counts << (sourceCount × targetCount)

**Cross-Domain Example:**
```sparql
# GOOD: Specific FK match
SELECT (COUNT(*) as ?links) WHERE {
    ?sourceEntity source:Class.idProperty ?sharedId .
    ?targetEntity target:Class.idProperty ?sharedId .
}
# Result: Should be much less than (sourceCount × targetCount)
```

### Rule #4: Single Unified Cross-Domain Architecture
**Internal Linking:** One ontology per domain (e.g., `{UUID}/PLMLinking`, `{UUID}/CRMLinking`) + One layer per domain  
**Cross-Domain Adaptation:** **ONE unified ontology + ONE unified layer** for all cross-domain relationships

**Naming Convention:**
- **Ontology URI:** `{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking`
- **Layer Name:** "Cross-Domain Linking"
- **Properties:** Descriptive names indicating source and target domains
  - `relatedProductionOrder` (PLM → Manufacturing)
  - `relatedQualityIssue` (PLM → Quality)
  - `relatedCustomerFeedback` (Service → CRM)

**UUID Generation**: Use an 8-character hex UUID to ensure global uniqueness across graphmarts:
```python
import uuid
short_uuid = uuid.uuid4().hex[:8]
# Use in URI: f"{YOUR_BASE_URI}/ontologies/{short_uuid}/CrossDomainLinking"
```

**Rationale:**
- **Architectural Consistency:** Single ontology pairs with single layer
- **Simplicity:** One ontology + one layer to register, maintain, and version
- **Discoverability:** All cross-domain relationships in one place
- **Flexibility:** Easy to add new cross-domain properties without creating new ontologies
- **Operational Efficiency:** Single layer refresh for all cross-domain links

### Rule #5: Refresh Only, Never Reload
**Same as Internal:** Use `refresh_graphmart` exclusively, avoid `reload_graphmart`.

### Rule #6: Register Every Ontology
**Same as Internal:** ALWAYS register ontologies immediately after creation!

### Rule #7: Import All Domain Ontologies
**Cross-Domain Adaptation:** The unified CrossDomainLinking ontology imports **ALL domain ontologies**.

**Benefit:** Single import statement covers all cross-domain property domain/range references.

### Rule #8: Validate Coverage Percentages
**Internal Linking:** Expected 70-95% coverage for direct FKs  
**Cross-Domain Adjustment:** Accept 20-60% coverage for Phase 1 connectivity

**Coverage Tiers:**
- **Excellent (60%+):** Strong cross-domain relationship, consider additional properties
- **Good (40-60%):** Solid Phase 1 connection
- **Acceptable (20-40%):** Sufficient for minimum connectivity
- **Weak (<20%):** Consider alternative linking strategy

---

## Cross-Domain Linking Strategy: Minimum Spanning Tree

### Graph Theory Approach
**Objective:** Connect N domains with minimum edges (cross-domain links)  
**Minimum:** (N-1) cross-domain links required for full connectivity  
**Strategy:** Prioritize high-coverage, semantically rich connections

### Phase 1: Minimum Viable Connectivity
**Goal:** Create minimum spanning tree where every domain is reachable from every other domain via graph traversal.

**Success Criteria:**
- ✅ All domains reachable from any other domain via graph traversal
- ✅ No orphaned domain islands
- ✅ Minimum number of cross-domain links ((N-1) links for N domains)
- ✅ High data quality connections (>40% coverage preferred)

### Phase 2: Business-Driven Expansion
**Goal:** Add additional cross-domain relationships based on specific business questions and use cases.

---

## Methodology: 8-Step Process

### Step 1: Domain Discovery & Analysis
**Identify your domains and their key entities:**

1.1. **List Available Domains**
```bash
mcp_ags-sparql-ag_list_transformation_layers
# Look for internal linking layers to identify domains
# Example output: "PLM Internal Linking", "CRM Internal Linking", etc.
```

1.2. **Catalog Domain Entities**
For each domain, find the main entity types:
```sparql
PREFIX domain: <{DOMAIN_ONTOLOGY_URI}#>

SELECT ?type (COUNT(DISTINCT ?entity) as ?count)
WHERE {
    ?entity a ?type .
    FILTER(STRSTARTS(STR(?type), "{DOMAIN_ONTOLOGY_URI}#"))
}
GROUP BY ?type
ORDER BY DESC(?count)
```

1.3. **Identify Shared Identifier Patterns**
Look for properties that might match across domains:
```sparql
PREFIX domain: <{DOMAIN_ONTOLOGY_URI}#>

# Find all datatype properties (potential foreign keys)
SELECT DISTINCT ?property (COUNT(?entity) as ?usage)
WHERE {
    ?entity ?property ?value .
    FILTER(isLiteral(?value))
    FILTER(STRSTARTS(STR(?property), "{DOMAIN_ONTOLOGY_URI}#"))
}
GROUP BY ?property
ORDER BY DESC(?usage)
```

### Step 2: Cross-Domain Coverage Analysis
**For each potential domain pair:**

2.1. **Test Shared Identifier Overlap**
```sparql
PREFIX domain1: <{DOMAIN1_ONTOLOGY_URI}#>
PREFIX domain2: <{DOMAIN2_ONTOLOGY_URI}#>

# Example: Test productId overlap between PLM and Manufacturing
SELECT 
    (COUNT(DISTINCT ?domain1Entity) as ?domain1Count)
    (COUNT(DISTINCT ?domain2Entity) as ?domain2Count)
    (COUNT(DISTINCT ?sharedId) as ?sharedIdCount)
    (COUNT(*) as ?totalPotentialLinks)
WHERE {
    ?domain1Entity domain1:EntityClass.idProperty ?sharedId .
    ?domain2Entity domain2:EntityClass.idProperty ?sharedId .
}
```

2.2. **Calculate Coverage Percentages**
```sparql
# Get baseline entity counts for each domain
SELECT (COUNT(?entity) as ?totalDomain1) WHERE { ?entity a domain1:EntityClass . }
SELECT (COUNT(?entity) as ?totalDomain2) WHERE { ?entity a domain2:EntityClass . }

# Calculate coverage:
# domain1Coverage = domain1Count / totalDomain1
# domain2Coverage = domain2Count / totalDomain2
```

2.3. **Create Coverage Matrix**
Document results in a table:
| Source Domain | Target Domain | Shared Attribute | Links | Source Coverage | Target Coverage | Priority |
|---------------|---------------|------------------|-------|-----------------|-----------------|----------|
| PLM | Manufacturing | productId | 45,000 | 72% | 90% | HIGH |
| PLM | Quality | productId | 15,000 | 60% | 30% | MEDIUM |
| Service | CRM | equipmentId | 8,000 | 80% | 65% | HIGH |

### Step 3: Design Minimum Spanning Tree
**Select cross-domain links for Phase 1:**

3.1. **Prioritize High-Coverage Links**
- Start with highest coverage percentages
- Ensure semantic validity (do the IDs actually represent the same entities?)
- Aim for business-critical connections

3.2. **Ensure Full Connectivity**
- Every domain must be reachable from every other domain
- Use graph theory: need exactly (N-1) edges for N nodes
- Check that your selected links form a connected graph

3.3. **Example Spanning Tree Strategy**
For 5 domains (PLM, Manufacturing, Quality, Service, CRM):
```
PLM (Central Hub)
├── Manufacturing (productId)
├── Quality (productId)  
└── Service (productId/equipmentId)
    └── CRM (equipmentId)
```
**Result:** 4 links connect 5 domains (minimum spanning tree)

### Step 4: Create Unified Cross-Domain Architecture (ONCE)
**Execute only ONCE before creating any cross-domain links:**

4.1. **Create Single Unified Ontology**
```bash
mcp_ags-sparql-ag_create_ontology
- ontology_uri: "{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking"
- ontology_label: "Cross-Domain Linking"
- description: "Defines relationships connecting entities across multiple domain ontologies"
- explanation: "Creating unified ontology for all cross-domain relationships"
```

4.2. **Register Ontology** (Don't forget!)
```bash
mcp_ags-sparql-ag_register_ontology
- ontology_uri: "{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking"
- explanation: "Registering cross-domain ontology with graphmart"
```

4.3. **Import All Domain Ontologies**
```bash
# For each domain ontology URI identified in Step 1
mcp_ags-sparql-ag_add_ontology_import
- ontology_uri: "{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking"
- imported_ontology_uri: "{DOMAIN_ONTOLOGY_URI}"
- explanation: "Importing {domain} ontology for cross-domain property references"
```

4.4. **Create Single Unified Transformation Layer**
```bash
mcp_ags-sparql-ag_create_transformation_layer
- layer_name: "Cross-Domain Linking"
- layer_description: "Connects entities across domains using shared identifiers"
- order_after: "{Last Internal Linking Layer}"
- explanation: "Creating unified layer for all cross-domain transformations"
```

### Step 5: Add Cross-Domain Properties
**For each cross-domain relationship in your spanning tree:**

5.1. **Create Object Property**
```bash
mcp_ags-sparql-ag_add_ontology_property
- ontology_uri: "{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking"
- property_uri: "{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking#{propertyName}"
- property_type: "object"
- property_label: "{Human Readable Label}"
- property_description: "Links {source_domain} {source_class} to {target_domain} {target_class} via {shared_attribute}"
- domain_uri: "{SOURCE_DOMAIN_ONTOLOGY_URI}#{SourceClass}"
- range_uri: "{TARGET_DOMAIN_ONTOLOGY_URI}#{TargetClass}"
- explanation: "Adding cross-domain property: {source_domain} → {target_domain}"
```

**Property Naming Convention**:
- `related{TargetDomainEntity}` (e.g., `relatedProductionOrder`, `relatedQualityIssue`)
- Include domain context when ambiguous
- Keep names descriptive and intuitive

### Step 6: Test Cross-Domain Queries
**CRITICAL**: Test each cross-domain query as SELECT before adding transformation steps!

6.1. **Validate Cross-Domain Query**
```sparql
PREFIX source: <{SOURCE_DOMAIN_ONTOLOGY_URI}#>
PREFIX target: <{TARGET_DOMAIN_ONTOLOGY_URI}#>

# Test the WHERE clause logic
SELECT ?sourceEntity ?targetEntity ?sharedId
WHERE {
    ?sourceEntity a source:SourceClass ;
        source:SourceClass.idProperty ?sharedId .
    ?targetEntity a target:TargetClass ;
        target:TargetClass.idProperty ?sharedId .
}
LIMIT 10
```

6.2. **Count Total Potential Links**
```sparql
SELECT (COUNT(*) as ?totalLinks)
WHERE {
    ?sourceEntity a source:SourceClass ;
        source:SourceClass.idProperty ?sharedId .
    ?targetEntity a target:TargetClass ;
        target:TargetClass.idProperty ?sharedId .
}
```

**Validation Checklist**:
- [ ] Total links match expected coverage from Step 2
- [ ] No cartesian product (links << sourceCount × targetCount)
- [ ] Sample results show semantically valid matches
- [ ] Query execution time reasonable (<5 seconds)

### Step 7: Add Cross-Domain Transformation Steps
**For each validated cross-domain relationship:**

7.1. **Create Transformation Step**
```bash
mcp_ags-sparql-ag_add_transformation_step
- layer_name_or_uri: "Cross-Domain Linking"
- step_name: "Link {SourceDomain} to {TargetDomain}"
- insert_query: "{SPARQL_INSERT_QUERY}"
- explanation: "Creating {source_domain} → {target_domain} links via {shared_attribute}"
```

7.2. **Transformation Query Template**
```sparql
PREFIX source: <{SOURCE_DOMAIN_ONTOLOGY_URI}#>
PREFIX target: <{TARGET_DOMAIN_ONTOLOGY_URI}#>
PREFIX xd: <{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking#>

INSERT {
    GRAPH ${targetGraph} {
        ?sourceEntity xd:{propertyName} ?targetEntity .
    }
}
${usingSources}
WHERE {
    # Use EXACT WHERE clause from validated SELECT query
    ?sourceEntity a source:SourceClass ;
        source:SourceClass.idProperty ?sharedId .
    ?targetEntity a target:TargetClass ;
        target:TargetClass.idProperty ?sharedId .
}
```

**Critical Template Requirements**:
- Use `${targetGraph}` (single $, not $$)
- Use `${usingSources}` (single $, not $$)
- Use `\n` for line breaks (not `\\n`)
- Copy exact WHERE clause from validated SELECT query

### Step 8: Execute and Validate Cross-Domain Links

8.1. **Refresh Graphmart**
```bash
mcp_ags-sparql-ag_refresh_graphmart
- explanation: "Executing cross-domain linking transformations"
```

8.2. **Validate Link Creation**
```sparql
PREFIX xd: <{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking#>

# Count links for each cross-domain property
SELECT ?property (COUNT(*) as ?linkCount)
WHERE {
    ?source ?property ?target .
    FILTER(STRSTARTS(STR(?property), "{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking#"))
}
GROUP BY ?property
```

8.3. **Test Cross-Domain Navigation**
```sparql
PREFIX source: <{SOURCE_DOMAIN_ONTOLOGY_URI}#>
PREFIX target: <{TARGET_DOMAIN_ONTOLOGY_URI}#>
PREFIX xd: <{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking#>

# Example: Navigate from source domain to target domain
SELECT ?sourceName ?targetName
WHERE {
    ?sourceEntity a source:SourceClass ;
        source:SourceClass.nameProperty ?sourceName ;
        xd:{propertyName} ?targetEntity .
    ?targetEntity target:TargetClass.nameProperty ?targetName .
}
ORDER BY ?sourceName
LIMIT 10
```

8.4. **Test Full Graph Connectivity**
```sparql
# Example: Multi-hop query across multiple domains
PREFIX domain1: <{DOMAIN1_ONTOLOGY_URI}#>
PREFIX domain2: <{DOMAIN2_ONTOLOGY_URI}#>
PREFIX domain3: <{DOMAIN3_ONTOLOGY_URI}#>
PREFIX xd: <{YOUR_BASE_URI}/ontologies/{SHORT_UUID}/CrossDomainLinking#>

SELECT ?domain1Entity ?domain2Entity ?domain3Entity
WHERE {
    ?domain1Entity a domain1:EntityClass ;
        xd:relatedDomain2Entity ?domain2Entity .
    ?domain2Entity xd:relatedDomain3Entity ?domain3Entity .
}
LIMIT 5
```

---

## 📊 Success Metrics

### Connectivity Metrics
- ✅ **All domains reachable** - Every domain can reach every other domain via traversal
- ✅ **Minimum links achieved** - Used (N-1) links for N domains (no over-linking)
- ✅ **No orphaned islands** - No domains completely isolated

### Coverage Metrics  
- ✅ **40%+ cross-domain coverage** - Excellent for Phase 1
- ✅ **20-40% cross-domain coverage** - Good for Phase 1
- ⚠️ **<20% cross-domain coverage** - Consider alternative linking strategies

### Business Value Metrics
- ✅ **Cross-domain queries enabled** - Can answer questions spanning multiple domains
- ✅ **Graph traversal possible** - Can navigate from any domain to any other domain
- ✅ **Analytics enhanced** - Can perform cross-domain aggregations and correlations

---

## 🚨 Common Pitfalls & Solutions

| Problem | Cross-Domain Specific Solution |
|---------|--------------------------------|
| **Low cross-domain coverage** | Expected! Start with best available links, improve in Phase 2 |
| **Identifier name mismatches** | Map variations (`productId` vs `partNumber`) in transformation |
| **Multiple ontologies created** | Use single unified CrossDomainLinking ontology for all links |
| **Data quality issues** | Document gaps, focus on semantic validity over perfect coverage |
| **Complex business rules** | Start simple (direct ID matching), add complexity in Phase 2 |
| **Performance degradation** | Cross-domain queries are naturally slower - optimize as needed |
| **Forgot domain imports** | Import ALL domain ontologies into the unified cross-domain ontology |

---

## 🎯 Quick Reference Template

**Replace these placeholders in all examples**:
- `{YOUR_BASE_URI}` - Your organization's base URI (e.g., `http://company.com`)
- `{SHORT_UUID}` - 8-character hex UUID for global uniqueness (e.g., `7b4d8f2a`)
- `{DOMAIN_ONTOLOGY_URI}` - Individual domain ontology URI  
- `{SOURCE_DOMAIN_ONTOLOGY_URI}` / `{TARGET_DOMAIN_ONTOLOGY_URI}` - Specific domain URIs
- `{propertyName}` - Your cross-domain property name
- `{SourceClass}` / `{TargetClass}` - Entity class names from different domains
- `{shared_attribute}` - The identifier used for matching across domains

**Generate UUID**:
```python
import uuid
short_uuid = uuid.uuid4().hex[:8]  # e.g., "7b4d8f2a"
```

**Example Cross-Domain URIs**:
- Cross-Domain Ontology: `http://company.com/ontologies/7b4d8f2a/CrossDomainLinking`  
- Property: `http://company.com/ontologies/7b4d8f2a/CrossDomainLinking#relatedProductionOrder`
- Layer: "Cross-Domain Linking"

**Architecture Pattern**:
```
Domain 1 Internal Linking ──┐
Domain 2 Internal Linking ──┤
Domain 3 Internal Linking ──┼─→ Cross-Domain Linking (Unified Layer)
Domain 4 Internal Linking ──┤
Domain 5 Internal Linking ──┘
```

This creates a clean separation between internal domain linking (multiple layers) and cross-domain linking (single unified layer).