Garnet Framework has evolved with architectural improvements for better cost efficiency and scalability. This documentation is current and reflects these changes, though we continue to expand certain sections with additional examples and detailed guidance.
Thank you for your patience as we complete this update.
Design Considerations
When building solutions with the Garnet Framework, you need to make several key design decisions about how to model and structure your knowledge graph. These considerations apply across all components of your system - from Context Producers that ingest data to Context Consumers that act upon it.
This section provides guidance on design patterns and best practices that will help you build robust, scalable, and maintainable solutions with Garnet Framework.
Understanding Entity Identity
Entity identification means assigning unique identifiers to the concepts you want to represent in your knowledge graph. When your Context Producer receives data, it needs to determine which entity (or entities) in the knowledge graph should be updated with this new information. Without consistent identification, you might end up with multiple entities representing the same concept.
NGSI-LD entities must have unique identifiers, and we recommend the following pattern:
urn:ngsi-ld:{Type}:{uniqueIdentifier}
Where:
{Type}is a flexible category that indicates the nature or classification of the entity. When using multi-typing, choose the type that makes the most sense for your system's identification needs{uniqueIdentifier}is a unique string that distinguishes this specific entity from all others of the same type
The key is establishing a reliable mapping between your data source identifiers and these URNs. This ensures that the same real-world object or concept always maps to the same entity identifier.
{
"id": "urn:ngsi-ld:AirQualityObserved:AIQ-0001",
"type": "AirQualityObserved",
"co2": {
"value": 450,
"unitCode": "PPM",
"observedAt": "2025-06-22T14:30:00Z"
},
"pm25": {
"value": 12.5,
"unitCode": "GQ",
"observedAt": "2025-06-22T14:30:00Z"
},
"measuredBy": {
"type": "Relationship",
"object": "urn:ngsi-ld:AirQualitySensor:AIQ-0001",
"objectType": "AirQualitySensor"
}
}
In this example, both the observation entity and the sensor entity use the same identifier base (AIQ-0001) from your data source. This consistent identifier strategy allows your Context Producer to automatically establish the relationship between measurements and their sensors.
Entity Types and Classification
Your choice of entity types creates the vocabulary that applications use to understand and query your data. This decision impacts both the semantic clarity of your system and the efficiency of your queries.
Rather than choosing between specific types like AirQualitySensor versus generic types like Sensor, consider using NGSI-LD's multi-typing capabilities. For example, an air quality sensor could have types ["Sensor", "AirQualitySensor", "IndoorAirQualitySensor"], enabling applications to query at different levels of specificity.
This approach allows your building management system to query broadly for any Sensor when performing general operations, narrow down to AirQualitySensor for air quality monitoring functions, or target IndoorAirQualitySensor specifically when needed. Multi-typing provides flexibility without losing semantic precision, as each type adds a layer of classification that applications can leverage based on their specific requirements.
Entity Granularity
A key decision is determining what constitutes an entity versus what should be an attribute. Some concepts are strong enough to warrant their own entity, while others are better represented as attributes of a main entity.
Consider the complexity and importance of each concept in your domain:
Entity-Worthy Concepts: Create separate entities when a concept is complex enough to have its own identity, lifecycle, or relationships. For example, you might create separate AirQualityObserved entities linked to AirQualitySensor entities through measuredBy relationships when observations have complex metadata, come from multiple sources, or need to be referenced by other entities.
Attribute-Level Concepts: Include simpler concepts directly as attributes of the main entity. For example, a ParkingSpot entity might have status, occupiedSince, and batteryLevel attributes that get updated with each LoRaWAN message from the parking sensor. These concepts are naturally part of the parking spot and don't need independent existence.
Evolving Decisions: Your modeling choices can evolve over time as your understanding of the domain deepens or requirements change. A simple attribute might later become complex enough to warrant its own entity type, or you might discover that separate entities should be consolidated into attributes.
There's no single correct answer - the decision depends on how complex the concept is, how it's used in your applications, and how it relates to other concepts in your domain.
Relationship Design
The relationships you establish between entities create the knowledge graph's structure and determine how information flows through your system. A key consideration is deciding when a concept should be an attribute versus when it should become a separate entity connected through relationships.
Consider how concepts can evolve in complexity. A simple status attribute on a Task entity might initially be sufficient for basic tracking. However, as your system grows, you might discover that status information needs its own workflow rules, approval processes, or connections to audit systems. At that point, the status concept becomes complex enough to warrant its own TaskStatus entity, linked to tasks through a hasStatus relationship.
This evolution from attribute to entity-with-relationship is common as systems mature. What starts as a simple property can develop into a rich concept that benefits from independent existence, its own attributes, and connections to other entities in your knowledge graph.
The key is designing relationships that reflect both the current complexity of your domain and allow for future evolution as your understanding deepens.
Multiple Relationships and Ordering
When entities need to connect to multiple related entities, NGSI-LD provides flexible options for representing these connections:
Array of Relationships: Use an array in the object field when you need to link to multiple entities but order doesn't matter. For example, a Room entity might have a containsSensors relationship with an array of sensor URIs: ["urn:ngsi-ld:Sensor:temp001", "urn:ngsi-ld:Sensor:humid001"].
ListRelationship: When the order of relationships matters, use the ListRelationship type instead of regular Relationship. This preserves the sequence, which is important for scenarios like process steps, priority rankings, or sequential workflows. For example, a BusLine entity might have an ordered list of bus stops through a busStops ListRelationship:
{
"id": "urn:ngsi-ld:BusLine:Line-101",
"type": "BusLine",
"busStops": {
"type": "ListRelationship",
"objectList": [
"urn:ngsi-ld:BusStop:Stop1",
"urn:ngsi-ld:BusStop:Stop2",
"urn:ngsi-ld:BusStop:Stop3"
],
"objectType": "BusStop"
}
}
This ordered representation enables applications to retrieve the complete bus line information including the sequence of stops, combining the ordered relationships with the geographical properties of each bus stop entity.
Consider these patterns when designing your Context Producer's data transformation logic, as the choice affects how consuming applications can query and process the relationship data.
Advanced Patterns: Aggregated Views
Context Producers can implement sophisticated aggregation patterns that transform how applications access and use contextual information. Rather than forcing applications to piece together data from multiple related entities, you can create Context Producers that maintain aggregated views alongside detailed data.
Consider a smart building where each room contains multiple sensors. Air quality sensors measure temperature, humidity, and CO2 levels, while occupancy sensors detect presence and movement patterns. In a traditional approach, each measurement would create separate observation entities - AirQualityObserved and OccupancyObserved - each linked to their respective sensors through measuredBy relationships.
While this detailed approach preserves all measurement history and enables sophisticated temporal analysis, it creates a challenge for applications that simply need to know the current state of a room. A dashboard showing room conditions would need to query multiple entity types, follow relationships, and aggregate the results - a complex and potentially slow operation.
The aggregated view pattern solves this by using subscriptions to automatically maintain summary information. When you create subscriptions for AirQualityObserved and OccupancyObserved entities, these notifications can trigger a Lambda function that acts as a specialized Context Producer. This function receives real-time updates about new observations and uses them to update Room entities with current environmental conditions and occupancy status.
The key insight is that this aggregation preserves data lineage through providedBy relationships. The Room entity's temperature attribute can still indicate which sensor provided the measurement, maintaining the connection to the detailed observation data while offering immediate access to current conditions.
This pattern creates a knowledge graph that serves multiple access patterns efficiently. Applications needing detailed historical analysis can work with the individual observation entities, while applications requiring quick access to current room status can query the aggregated Room entities directly. The result is a system that supports both analytical depth and operational speed without compromising either capability.