Skip to main content

API Property Embedding Strategy

Approved Approach

Frontend will NOT know about LinkProperty/NodeProperty tables.

Properties are embedded in asset DTOs (Pump, Valve, Tank) via JOIN operations in the controller layer.


🎯 The Pattern

What Frontend Sees

// Single API call
const pump = await fetch('/api/pumps/Pump-1');

// Complete response with all properties
{
"id": "Pump-1",
"fromNode": "J-100",
"toNode": "J-200",
"initStatus": 1, // From LinkProperty table
"initSetting": 1.0, // From LinkProperty table
"pattern": 5, // From LinkProperty table
"hCurve": 10, // From LinkProperty table
"eCost": 0.05, // From LinkProperty table
"eCurve": 3, // From LinkProperty table
"ePattern": 2 // From LinkProperty table
}

What Happens in the Backend

[HttpGet("{id}")]
public async Task<ActionResult<PumpDto>> GetPump(string id)
{
// Step 1: Get core entity
var pump = await _context.Pumps
.FirstOrDefaultAsync(p => p.Id == id);

if (pump == null)
return NotFound();

// Step 2: Get all LinkProperties for this pump
var properties = await _context.LinkProperties
.Where(lp => lp.LinkId == id)
.ToDictionaryAsync(lp => lp.PropertyId, lp => lp.PropertyValue);

// Step 3: Map both to DTO (embedding properties)
return Ok(MapToDto(pump, properties));
}

private PumpDto MapToDto(Pump pump, Dictionary<int, double> properties)
{
return new PumpDto
{
Id = pump.Id,
FromNode = pump.FromNode,
ToNode = pump.ToNode,

// Embed properties from LinkProperty table
InitStatus = GetIntProperty(properties, EN_LinkProperty.EN_INITSTATUS),
InitSetting = GetProperty(properties, EN_LinkProperty.EN_INITSETTING),
Pattern = GetIntProperty(properties, EN_LinkProperty.EN_LINKPATTERN),
HCurve = GetIntProperty(properties, EN_LinkProperty.EN_PUMP_HCURVE),
ECost = GetProperty(properties, EN_LinkProperty.EN_PUMP_ECOST),
ECurve = GetIntProperty(properties, EN_LinkProperty.EN_PUMP_ECURVE),
EPattern = GetIntProperty(properties, EN_LinkProperty.EN_PUMP_EPAT),

CreatedAt = pump.CreatedAt,
ModifiedAt = pump.ModifiedAt
};
}

// Helper methods for property extraction
private double? GetProperty(Dictionary<int, double> props, EN_LinkProperty propId)
{
return props.TryGetValue((int)propId, out var value) ? value : null;
}

private int? GetIntProperty(Dictionary<int, double> props, EN_LinkProperty propId)
{
return props.TryGetValue((int)propId, out var value) ? (int)value : null;
}

Benefits of This Approach

Frontend Simplicity

  • Single API call gets everything
  • No knowledge of property tables required
  • Consistent DTO structure
  • Easy to work with

Backend Flexibility

  • Can add new properties without breaking API
  • Properties stored normalized in database
  • JOIN performance acceptable
  • Clear separation of concerns

Maintainability

  • Backend owns the complexity
  • Frontend stays simple
  • Easy to understand and debug
  • Standard REST patterns

Alternative Approaches (Rejected)

❌ Option: Expose LinkProperty/NodeProperty Tables

// Frontend would need TWO calls
const pump = await fetch('/api/pumps/Pump-1');
const properties = await fetch('/api/linkproperties?linkId=Pump-1');

// Then manually merge
const completePump = { ...pump, ...parseProperties(properties) };

Why Rejected:

  • Exposes internal database structure
  • Requires frontend to understand property IDs
  • More API calls = slower
  • Complex frontend logic
  • Poor developer experience

Implementation Notes

Property ID Mapping

Link Property IDs (EN_LinkProperty enum):

  • 4: EN_INITSTATUS
  • 5: EN_INITSETTING
  • 14: EN_PUMP_HCURVE
  • 15: EN_LINKPATTERN
  • 20: EN_PUMP_ECURVE
  • 21: EN_PUMP_ECOST
  • 22: EN_PUMP_EPAT

Node Property IDs (EN_NodeProperty enum):

  • 3: EN_EMITTER
  • 4: EN_INITQUAL
  • 23: EN_TANK_KBULK

Default Value Handling

If LinkProperty/NodeProperty record doesn't exist:

  • Return null (frontend interprets as default)
  • Or return known default value
  • Document defaults in API response

Summary

Chosen Strategy:

  • Properties embedded in DTOs
  • Frontend stays simple
  • Backend handles complexity
  • Single API calls
  • Clean abstraction

Result:

  • Better developer experience
  • Consistent API design
  • Maintainable codebase
  • Flexible for future changes

Related Documentation:

  • DTO Inheritance Analysis: DTO_Inheritance_Analysis.md
  • API Endpoints Reference: APIEndpoints.md
  • Configuration API: ConfigurationAPI.md

Status: ✅ Approved and Implemented Last Updated: October 2025