Skip to main content

DataEngine (Write Layer)

Purpose: centralize write operations that should emit standard events, so indexing and other subscribers remain consistent regardless of where writes originate.

API

  • setCustomFields({ entityId, recordId, organizationId?, tenantId?, values, notify? })
    • Persists custom field values (using definitions where available).
    • Emits <module>.<entity>.updated with { id, organizationId, tenantId } when notify !== false (default true).

DI registration

  • Resolved via dataEngine from the request container:
    • packages/shared/src/lib/di/container.ts
    • Default implementation: DefaultDataEngine in packages/shared/src/lib/data/engine.ts.

Usage

  • CRUD routes (already wired):
    • packages/shared/src/lib/crud/factory.ts calls dataEngine.setCustomFields(...) on create/update when custom fields are enabled.
  • Manual scripts / CLIs:
    • Resolve and call dataEngine.setCustomFields(...) instead of writing CFs directly.

See also

Example

const { resolve } = await createRequestContainer()
const de = resolve('dataEngine') as DataEngine
await de.setCustomFields({
entityId: 'example:todo',
recordId: todoId,
organizationId: orgId,
tenantId,
values: { priority: 3, severity: 'high' },
})

Why this matters

  • Guarantees a single, coherent source of CRUD-related events used by the indexing layer and other subscribers.
  • Avoids UI-level onChanged hooks for CF writes; events are emitted at the data layer.

Extending

  • You can add create, update, and delete helpers to DataEngine to funnel all entity mutations through the same layer (for audit, validation, or derived writes) while keeping module boundaries clean.

Related files

  • packages/shared/src/lib/data/engine.ts
  • packages/shared/src/lib/di/container.ts: queryEngine, dataEngine
  • packages/shared/src/lib/crud/factory.ts
  • packages/example/src/modules/example/cli.ts