Selegic CRM Docs
ServerDynamic Objects

Architecture

Data flow, service relationships, and layer composition for Dynamic Objects.

Request Flow

POST /api/v1/objects/:entity/createOne getFields(entity, tenantId) Field Definitions validate(params, fields) Validated Data handle(entity, op, data) runPreFlows() execute(entity, op, data) INSERT / UPDATE / SELECT result result runPostFlows() result JSON Response Client Dynamic Routes ValidationService EntityMetadataService OperationHandlerService DynamicRepository Tenant Database

Layered Architecture

Unlike CRUD which uses a global layer, Dynamic Objects use a Request-Scoped Layer built via buildDynamicObjectsLayer.

1. Discovery Layer (EntityMetadataService)

  • Fetches entity and field definitions from Entity and Field tables
  • Caches definitions to avoid DB lookups per request
  • Handles field type resolution (PICKLIST, LOOKUP, FORMULA)

2. Validation Layer (ValidationService)

  • Converts Field definitions to JSON Schema using AJV
  • Caches validators using (tenantId, entityName) key
  • Invalidates cache on field/entity changes

3. Execution Layer (DynamicRepository)

  • Uses dynamic Prisma calls or raw SQL for tenant tables
  • Maintains Prisma-like syntax for consistency
  • Handles tenant-specific connection

4. Orchestration Layer (OperationHandlerService)

  • Coordinates pre-flows, hooks, repository, post-flows
  • Handles formula field computation
  • Manages audit field updates

Request-Scoped Layer

Dynamic Objects build their layer per-request:

import { buildDynamicObjectsLayer } from "./layers";

app.use("*", async (c, next) => {
  const org = c.get("org");
  
  // Build tenant-specific layer
  const layer = await buildDynamicObjectsLayer(org);
  
  // Execute route with layer
  await next();
});

This ensures each tenant gets their own:

  • Prisma client connected to their database
  • Cached metadata
  • Isolated execution context

On this page