Skip to content

Conventions

An app's layout

<app>/                         # the repository root, or apps/<app>/ in a monorepo
  ddcore.app.ts                  defineApp: title, roles, scheduler, docEvents, desk, afterInstall, fixtures
  doctypes/<snake>/
    <snake>.doctype.ts          defineDoctype (meta)
    <snake>.controller.ts       defineController (rules, methods)
    <snake>.form.ts             defineForm (desk) — imports "@ddcore/desk-sdk"
    <snake>.test.ts             tests (imports "@ddcore/sdk/test")
  extensions/<snake>.extend.ts   extendDoctype: fields and properties added to another app's DocType
  extensions/<snake>.form.ts     defineForm for that DocType, loaded alongside its owner's
  services/*.ts                 business functions; whitelisted() exposes them at /api/method/<app>.services.<file>.<fn>
  reports/*.report.ts           defineReport
  workspaces/*.workspace.ts     defineWorkspace
  workflows/<name>.workflow.ts  defineWorkflow: states, transitions, approval roles — see `workflows`
  print/<name>.print.ts         a print format — see `print`
  patches/NNNN_name.ts          definePatch({ phase, execute }) — runs once, during migrate → migrations
  client/*.ts                   scripts loaded across the whole desk (declare them in desk.include)
  translations/pt-BR.csv        "key,translation,# source" — generated by `ddcore i18n extract`
  .ddcore/types.d.ts             generated by `ddcore types` — import with `import type { Contract } from "../../.ddcore/types"`

A module's dotted path is <app>.<folder>.<file> (no .ts). For example: my_app.services.tasks.run. That is the form used by whitelisted, scheduler, ddcore.enqueue, ddcore exec and the call_method tool.

What the app puts on the desk

defineApp's desk block is the app's say over the shell around its screens:

ts
desk: {
  include: ["client/lists.ts"],   // scripts loaded on every desk page
  home: "Projects",               // the workspace /app opens on
  logo: "📁",                     // the square mark beside the site's name
}

home and logo are resolved the same way: the first app in load order that declares one wins, and the core declares neither, so an app's always does.

logo is one letter or one emoji, not an image URL — the mark is a fixed square, rendered as text. Leave it out and the mark is the initial of the name printed beside it, which is this app's title. That title is a catalogue key like any label, so both the word and its initial follow the reader's language — see i18n.

Naming

  • DocType name: ASCII, spaces allowed, capitalised ("Project Milestone"). Becomes the table tab_project_milestone and the interface ProjectMilestone.
  • fieldname: ASCII snake_case (due_date).
  • Labels are English, because a label is a catalogue key. An accent belongs in translations/<lang>.csv, never in the code. See i18n.
  • Reserved: name, owner, creation, modified, modified_by, docstatus, doctype, parent, parenttype, parentfield, idx.

Rules that do not change

  • Server code is synchronous. ddcore.db.getValue(...) returns the value directly; never await in a controller or a service.
  • Business errors: ddcore.throw(_("message"), { title: _("Title") }) — becomes HTTP 417 ValidationError and a toast in the desk.
  • Once submitted (docstatus 1), only fields with allowOnSubmit change through save(); everything else goes through doc.dbSet(...).
  • A derived status (paid, overdue, …) is computed in validate, never typed.
  • The sidebar and the workspace are explicit (defineWorkspace), never inferred.
  • Never edit .ddcore/types.d.ts; run ddcore types.
  • A renamed field or DocType declares renamedFrom; without it the rename is a new empty column and an orphaned old one. See migrations.
  • A fieldtype change that could lose data is refused, not guessed: declare convert, or move the data across releases with expand → backfill → validate → contract.
  • A DocType belongs to one app: it is declared once, and another app adds to it with extendDoctype. See extending.
  • Tests: each it runs in a rolled-back transaction; create the data you need inside the test.
  • A Select's values are canonical English and are what the database holds; colour them with optionColors, never by matching their text.

Versioning and releases

The framework follows Semantic Versioning 2.0.0 (vMAJOR.MINOR.PATCH):

  • Git tags: Official releases use the v* format (e.g. v0.1.0, v1.0.0).
  • Release artifacts:
    • Pushing a version tag v* triggers the Release GitHub Action, creating a GitHub Release with cross-platform static archives (ddcore-<os>-<arch>.tar.gz and SHA256SUMS) for Darwin and Linux (amd64/arm64).
    • Pushing to main updates the rolling release latest used by the installer script (install.sh).
    • No container image is published: an app builds its own image and downloads the binary of the release it pins.
  • App compatibility (requires):
    • Apps declare compatibility constraints in ddcore.app.ts (requires: { ddcore: ">=0.1.0" }).
    • Breaking changes to public server/desk SDKs or engine contracts increment MAJOR.
    • New backwards-compatible capabilities or hooks increment MINOR.
    • Bug fixes and optimizations increment PATCH.

MIT Licensed · ddcore (Data Driven Core) · Development documentation (main)