Skip to main content

Object Management Documentation

Module: Object Registry & Profile Configuration
Version: 1.0
Last Updated: March 12, 2026


📌 Overview

Object Management handles the dynamic entity configuration for Axiom Genesis. It defines:

  • What data exists: table/collection mappings, field definitions
  • How to access it: field-level policies, masking, projection
  • Who can see what: role-based field visibility and row-level access

Core Tables:

  • app_object_registries - Entity metadata and table mappings
  • app_object_profiles - Field-level access policies and configurations

app_object_registries - Entity Registry

Purpose

Maps business entities (CUSTOMERS, ORDERS, PRODUCTS) to actual database tables/collections and stores metadata about fields and relationships.

Schema

SELECT
obj_id (INT), -- Primary Key
obj_tenant_id (INT), -- Tenant isolation
obj_code (VARCHAR(256)), -- Entity identifier (e.g., 'CUSTOMERS')
obj_description (VARCHAR), -- Display name
obj_type_code (VARCHAR), -- Type: 'BUSINESS', 'SYSTEM', 'VIEW'
obj_connection_code (VARCHAR), -- Links to app_connections
obj_payload (JSON), -- Field metadata & definitions
obj_payload_object_name (NVARCHAR), -- Actual table name (e.g., 'app_customers')
obj_payload_identifier (NVARCHAR), -- Primary key field (e.g., 'cus_id')
obj_payload_column_prefix (NVARCHAR), -- Field prefix (e.g., 'cus_')
obj_payload_table (NVARCHAR), -- Source table name
obj_status (INT), -- 1=Active, 0=Inactive
obj_is_deleted (BIT), -- Soft delete flag
obj_audit_data (JSON), -- {created_by, created_at, updated_by, ...}
obj_add_dtls (JSON) -- Additional metadata
FROM app_object_registries
WHERE obj_status = 1 AND obj_is_deleted = 0

obj_payload Structure (JSON Column)

The obj_payload column contains complete field metadata:

{
"object_code": "CUSTOMERS",
"object_name": "Customer Management",
"table_name": "app_customers",
"column_prefix": "cus",
"identifier": "cus_id",
"fields": [
{
"field_code": "cus_id",
"field_name": "Customer ID",
"field_type": "integer",
"is_primary_key": true,
"is_required": false,
"is_read_only": true,
"data_type": "INT",
"description": "Unique customer identifier"
},
{
"field_code": "cus_name",
"field_name": "Customer Name",
"field_type": "string",
"is_required": true,
"is_searchable": true,
"data_type": "VARCHAR(256)",
"validation": {
"min_length": 1,
"max_length": 256
}
},
{
"field_code": "cus_email",
"field_name": "Email Address",
"field_type": "email",
"is_required": true,
"is_searchable": true,
"data_type": "VARCHAR(256)",
"validation": {
"pattern": "^[^@]+@[^@]+\\.[^@]+$"
}
},
{
"field_code": "cus_status",
"field_name": "Status",
"field_type": "select",
"is_required": true,
"data_type": "VARCHAR(50)",
"options": ["ACTIVE", "INACTIVE", "SUSPENDED"],
"default_value": "ACTIVE"
},
{
"field_code": "cus_metadata",
"field_name": "Metadata",
"field_type": "json",
"data_type": "JSON",
"description": "Extensible metadata storage"
}
],
"relationships": [
{
"relationship_code": "CUSTOMER_ORDERS",
"relationship_name": "Customer Orders",
"related_entity": "ORDERS",
"relationship_type": "one_to_many",
"foreign_key": "ord_customer_id"
}
]
}

Middleware Usage Pattern (ReadService)

// From: services/entity/read.service.js
ReadService.get('CUSTOMERS', { status: 'ACTIVE' }, user)

1. Load from Redis: registry:${tenant_id}:CUSTOMERS
2. Cache miss → Query app_object_registries
SELECT obj_payload, obj_payload_identifier, obj_payload_table
FROM app_object_registries
WHERE obj_code = 'CUSTOMERS' AND obj_tenant_id = @tenantId

3. Extract from obj_payload:
- fields[] array for field definitions
- relationships[] for linked entities
- column_prefix ('cus') to auto-detect tenant field ('cus_tenant_id')

4. Use obj_payload_table ('app_customers') as SQL table name
5. Use obj_payload_identifier ('cus_id') as primary key
6. Cache in Redis for 24 hours

// Example: Field detection from payload
const registry = {
obj_payload: {
fields: [
{ field_code: 'cus_id', is_primary_key: true, ... },
{ field_code: 'cus_name', is_required: true, ... },
{ field_code: 'cus_status', field_type: 'select', ... }
]
}
}

// Service extracts field information:
- Required fields: cus_name
- Primary key: cus_id
- Selectable fields: cus_status

Real Example: CUSTOMERS Entity

{
"obj_id": 101,
"obj_tenant_id": 1,
"obj_code": "CUSTOMERS",
"obj_description": "Customer Management",
"obj_type_code": "BUSINESS",
"obj_connection_code": "DEFAULT_TENANT_DB",
"obj_payload_object_name": "app_customers",
"obj_payload_identifier": "cus_id",
"obj_payload_column_prefix": "cus",
"obj_payload_table": "app_customers",
"obj_status": 1,
"obj_is_deleted": 0,
"obj_payload": {
"object_code": "CUSTOMERS",
"object_name": "Customer Management",
"table_name": "app_customers",
"column_prefix": "cus",
"identifier": "cus_id",
"fields": [
{
"field_code": "cus_id",
"field_name": "Customer ID",
"field_type": "integer",
"is_primary_key": true,
"is_read_only": true,
"data_type": "INT"
},
{
"field_code": "cus_name",
"field_name": "Customer Name",
"field_type": "string",
"is_required": true,
"data_type": "VARCHAR(256)"
},
{
"field_code": "cus_email",
"field_name": "Email",
"field_type": "email",
"is_required": true,
"data_type": "VARCHAR(256)"
},
{
"field_code": "cus_status",
"field_name": "Status",
"field_type": "select",
"data_type": "VARCHAR(50)",
"options": ["ACTIVE", "INACTIVE"]
}
]
},
"obj_audit_data": {
"created_by": "admin",
"created_at": "2026-01-15T08:00:00Z",
"updated_by": "admin",
"updated_at": "2026-03-10T14:30:00Z"
}
}

app_object_profiles - Field-Level Policies

Purpose

Defines how fields are accessed, including visibility, masking, and write permissions. Profiles are applied per user/role to control:

  • Projection: Which fields to include/exclude
  • Masking: How to obfuscate sensitive fields (email, phone, SSN)
  • Row Filters: Which records user can access (department = @user.department)
  • Write Control: Which fields are editable

Schema

SELECT
prf_id (INT), -- Primary Key
prf_tenant_id (INT), -- Tenant isolation
prf_code (VARCHAR(256)), -- Profile identifier
prf_name (VARCHAR), -- Display name
prf_object_code (VARCHAR), -- Links to app_object_registries
prf_type (VARCHAR), -- 'view', 'edit', 'admin'
prf_policy (JSON), -- Access control policy
prf_is_default (BIT), -- 1=Use for all users, 0=Specific to role/user
prf_config (JSON), -- Additional configuration
prf_status (INT), -- 1=Active, 0=Inactive
prf_is_deleted (BIT), -- Soft delete flag
prf_audit_data (JSON), -- Audit trail
prf_add_dtls (JSON) -- Additional metadata
FROM app_object_profiles
WHERE prf_status = 1 AND prf_is_deleted = 0

prf_policy Structure (JSON Column) - Complete Reference

{
"tenantPolicy": "auto", // 'auto' (filter by user tenant) | 'manual'

"projection": {
// Field visibility control
"type": "include", // 'include' all specified | 'exclude' specified
"fields": ["cus_id", "cus_name", "cus_email", "cus_status"],
"excludeFields": [
// Fields NEVER returned
"cus_ssn",
"cus_payment_card",
"cus_internal_notes"
]
},

"masking": {
// Field-level masking/obfuscation
"cus_email": "email_mask", // email → ****@****
"cus_phone": "phone_mask", // phone → ***-****-1234
"cus_ssn": "ssn_mask", // SSN → ***-**-1234
"cus_credit_card": "card_mask", // Card → ****-****-****-1234
"cus_date_of_birth": "date_mask" // DOB → masked or hidden
},

"rowFilters": {
// Row-level access control
"department": "@user.department", // Only show records matching user's dept
"region": "@user.region", // Only show records matching user's region
"status": ["ACTIVE", "PENDING"] // Only show in these statuses
},

"writableFields": [
// Fields user can edit
"cus_name",
"cus_email",
"cus_status"
],

"readOnlyFields": [
// Fields user can read but not edit
"cus_id",
"cus_created_date",
"cus_tenant_id"
],

"conditionalRules": [
// Dynamic rules based on context
{
"condition": "role === 'MANAGER'",
"action": "can_edit_all",
"fields": ["cus_name", "cus_email", "cus_status", "cus_credit_limit"]
},
{
"condition": "role === 'USER'",
"action": "can_edit",
"fields": ["cus_email", "cus_phone"]
}
],

"computedFields": [
// Derived fields (not stored in DB)
{
"field_code": "cus_full_info",
"expression": "`${cus_name} (${cus_email})`"
}
]
}

Middleware Usage Pattern (WriteService/ReadService)

// From: services/entity/write.service.js and services/entity/read.service.js

// Step 1: Load profile
const profile = await getProfile('CUSTOMERS', user.tenant_id);
// Query: SELECT prf_policy FROM app_object_profiles
// WHERE prf_object_code = 'CUSTOMERS' AND prf_tenant_id = @tenantId

// Step 2: Parse policy
const policy = JSON.parse(profile.prf_policy);

// Step 3: Apply policy to request

// FOR READS:
const results = await db.query(...);
results = results.map(record => {
// Remove excluded fields
policy.projection.excludeFields.forEach(field => {
delete record[field];
});

// Apply masking
Object.entries(policy.masking).forEach(([field, maskType]) => {
if (record[field]) {
record[field] = applyMask(record[field], maskType);
}
});

// Keep only allowed fields (include/exclude)
if (policy.projection.type === 'include') {
const allowed = policy.projection.fields;
Object.keys(record).forEach(field => {
if (!allowed.includes(field)) delete record[field];
});
}

return record;
});

// FOR WRITES:
const payload = {
cus_name: 'New Name',
cus_email: 'email@example.com',
cus_ssn: '123-45-6789' // ← User trying to edit SSN
};

// Filter to only writable fields
const filtered = {};
policy.writableFields.forEach(field => {
if (field in payload) {
filtered[field] = payload[field];
}
});
// Result: { cus_name, cus_email } (cus_ssn removed)

// Step 4: Apply row filters (WHERE clause)
// WHERE department = @user.department AND status IN ('ACTIVE', 'PENDING')

Real Example: CUSTOMERS Profile (Sales Rep Role)

{
"prf_id": 201,
"prf_tenant_id": 1,
"prf_code": "CUSTOMERS_SALES_REP",
"prf_name": "Customer - Sales Rep View",
"prf_object_code": "CUSTOMERS",
"prf_type": "edit",
"prf_is_default": 0,
"prf_status": 1,
"prf_policy": {
"tenantPolicy": "auto",
"projection": {
"type": "include",
"fields": [
"cus_id",
"cus_name",
"cus_email",
"cus_phone",
"cus_status",
"cus_credit_limit",
"cus_sales_rep"
],
"excludeFields": ["cus_ssn", "cus_internal_notes", "cus_cost_price"]
},
"masking": {
"cus_phone": "phone_mask",
"cus_email": "email_mask"
},
"rowFilters": {
"cus_sales_rep": "@user.id" // Only see own customers
},
"writableFields": ["cus_email", "cus_phone", "cus_status"],
"readOnlyFields": ["cus_id", "cus_credit_limit", "cus_sales_rep"]
}
}

Real Example: CUSTOMERS Profile (Admin Role)

{
"prf_id": 202,
"prf_tenant_id": 1,
"prf_code": "CUSTOMERS_ADMIN",
"prf_name": "Customer - Admin View",
"prf_object_code": "CUSTOMERS",
"prf_type": "admin",
"prf_is_default": 0,
"prf_status": 1,
"prf_policy": {
"tenantPolicy": "auto",
"projection": {
"type": "include",
"fields": [
"cus_id",
"cus_name",
"cus_email",
"cus_phone",
"cus_ssn",
"cus_status",
"cus_credit_limit",
"cus_sales_rep",
"cus_internal_notes"
]
},
"masking": {}, // No masking for admins
"rowFilters": {}, // Can see all records
"writableFields": [
// Can edit all fields
"cus_id",
"cus_name",
"cus_email",
"cus_phone",
"cus_ssn",
"cus_status",
"cus_credit_limit",
"cus_sales_rep",
"cus_internal_notes"
]
}
}

Masking Types Reference

Available masking functions applied in prf_policy.masking:

Mask TypeInput ExampleOutputUse Case
email_maskjohn.doe@example.comj***@****Email privacy
phone_mask555-123-4567*-**-4567Phone privacy
ssn_mask123-45-6789*--6789Social security numbers
card_mask4532-1234-5678-9999**-**-****-9999Credit cards
general_maskanyvaluemaskedGeneric masking
date_mask1990-05-15[REDACTED]Date of birth/sensitive dates
full_hidevalue[HIDDEN]Completely hide value

Querying Object Registries & Profiles

Get Entity Configuration for Editing

-- Fetch complete CUSTOMERS entity definition
SELECT
obj_code,
obj_description,
obj_payload,
obj_payload_identifier,
obj_payload_column_prefix
FROM app_object_registries
WHERE obj_code = 'CUSTOMERS'
AND obj_tenant_id = 1
AND obj_is_deleted = 0
AND obj_status = 1;

-- Extract fields from obj_payload JSON
SELECT
obj_code,
JSON_VALUE(obj_payload, '$.fields[0].field_code') AS first_field,
JSON_QUERY(obj_payload, '$.fields') AS all_fields
FROM app_object_registries
WHERE obj_code = 'CUSTOMERS';

Get User's Effective Profile

-- Find the profile applicable to user's role
SELECT
prf_code,
prf_type,
prf_policy
FROM app_object_profiles
WHERE prf_object_code = 'CUSTOMERS'
AND prf_tenant_id = 1
AND (
prf_is_default = 1
OR prf_code = (
-- Get role-specific profile
SELECT profile_code FROM user_roles
WHERE user_id = 'user123' AND role_id = 2
)
)
AND prf_is_deleted = 0
AND prf_status = 1;

List All Entities and Their Profiles

SELECT
r.obj_code,
r.obj_description,
COUNT(p.prf_id) as profile_count,
STRING_AGG(p.prf_type, ', ') as profile_types
FROM app_object_registries r
LEFT JOIN app_object_profiles p ON r.obj_code = p.prf_object_code
AND r.obj_tenant_id = p.prf_tenant_id
WHERE r.obj_tenant_id = 1
AND r.obj_is_deleted = 0
AND r.obj_status = 1
GROUP BY r.obj_code, r.obj_description
ORDER BY r.obj_code;

API Examples

Read CUSTOMERS (With Profile Applied)

GET /api/v1/customers?status=ACTIVE
Headers: {
'x-tenant-id': 'encrypted_value',
'Authorization': 'Bearer token'
}

Response:
{
"success": true,
"data": [
{
"cus_id": 1,
"cus_name": "Acme Corp",
"cus_email": "j****@****", // Masked per profile
"cus_status": "ACTIVE"
// cus_ssn not included (excluded per profile.projection)
// cus_internal_notes not included (admin-only field)
}
]
}

Create CUSTOMER

POST /api/v1/customers
Headers: {
'x-tenant-id': 'encrypted_value',
'Authorization': 'Bearer token'
}
Body: {
"cus_name": "New Company",
"cus_email": "contact@newco.com",
"cus_status": "ACTIVE"
// Cannot include cus_ssn (not in writableFields per profile)
}

Response:
{
"success": true,
"data": {
"cus_id": 102,
"cus_name": "New Company",
"cus_email": "contact@newco.com",
"cus_status": "ACTIVE",
"cus_tenant_id": 1,
"cus_audit_data": {
"created_by": "user123",
"created_at": "2026-03-12T10:30:00Z"
}
}
}

Caching Strategy

Registry Caching

Cache Key: registry:${tenantId}:${objectCode}
TTL: 24 hours
Example: registry:1:CUSTOMERS

Invalidation Triggers:

  • Update to app_object_registries → clear registry:1:CUSTOMERS
  • Update to obj_payload → clear registry:1:CUSTOMERS
  • Automatic on INSERT/UPDATE/DELETE

Profile Caching

Cache Key: profile:${tenantId}:${objectCode}
TTL: 24 hours
Example: profile:1:CUSTOMERS

Invalidation Triggers:

  • Update to app_object_profiles → clear profile:1:CUSTOMERS
  • Update to prf_policy → clear profile:1:CUSTOMERS

Do's and Don'ts

✅ DO

  • Define all fields in obj_payload.fields[] - Service layer needs complete field definitions
  • Use column_prefix consistently - Prefix must match table column naming (cus*, ord*, etc.)
  • Apply masking for sensitive fields - Always mask SSN, payment info, DoB in profiles
  • Test profiles by role - Verify each role's projection works as expected
  • Use row filters for tenant isolation - Auto-apply tenant constraints in policy
  • Cache registries and profiles - Expected 24h TTL, automatic invalidation on update
  • Document relationships in obj_payload - Include relationship_code and related_entity references
  • Version your profiles - Keep multiple profiles per entity for different roles/views

❌ DON'T

  • Hardcode field names in service code - Always read from obj_payload.fields[]
  • Forget to apply masking - Policy.masking should be applied before returning data
  • Use prf_policy in stored procedures - Policies applied at middleware service layer only
  • Create profiles without exclusions - Always explicitly exclude sensitive fields
  • Skip row-level filters - Even if all records visible, document why policy.rowFilters is empty
  • Cache profile longer than 24h - Risk stale policy being applied to requests
  • Modify obj_payload directly in code - Use configuration APIs to update metadata
  • Assume projection auto-applies - Service layer must explicitly implement projection logic

Common Issues & Solutions

IssueCauseSolution
Sensitive field visibleProjection doesn't exclude fieldAdd to prf_policy.projection.excludeFields
Masking not appliedprf_policy.masking entry missingAdd masking rule for field in profile
User sees all recordsRow filter missingAdd rowFilters constraint in policy
Write includes read-onlywritableFields not filteredApply writableFields filter in write service
Wrong table queriedobj_payload_table incorrectVerify obj_payload_table value matches actual table
Prefix mismatch errorcolumn_prefix ≠ actual columnsUpdate obj_payload_column_prefix to match table