Manifesto de domínio

central.domain.json declara models, campos, fórmulas e validações sem um arquivo JavaScript por model — o Core constrói schema, metadata, CRUD e regras protegidas.

O arquivo backend/central.domain.json é carregado durante o bootstrap, antes de src/models, src/validations, src/triggers e demais diretórios de extensão.

A Central declara o domínio e as regras. O OonCore constrói schema Mongoose, metadata, CRUD, cálculos protegidos e validações.

As expressões são interpretadas por um avaliador fechado. O Core não usa eval, Function nem executa código vindo do manifesto.

Exemplo

{
  "name": "Central SS Eventos",
  "slug": "ss-eventos",
  "schemaVersion": 1,
  "models": [
    {
      "name": "ProjetoItem",
      "singular": "item",
      "basePath": "/itens",
      "crud": { "enabled": true, "permissions": { "read": "itens.read", "write": "itens.write" } },
      "fields": {
        "quantidade": { "kind": "number", "required": true },
        "diarias": { "kind": "number", "required": true },
        "valorUnitario": { "kind": "currency", "required": true },
        "percentualFee": { "kind": "number", "default": 0 },
        "valorContratado": { "kind": "currency", "default": 0 },
        "valorPago": { "kind": "currency", "default": 0 },
        "statusIntegracao": { "kind": "string", "readonly": true },
        "subtotal": {
          "kind": "currency",
          "computed": {
            "precision": 2,
            "expression": { "op": "multiply", "args": [{ "field": "quantidade" }, { "field": "diarias" }, { "field": "valorUnitario" }] }
          }
        },
        "valorFee": {
          "kind": "currency",
          "computed": {
            "precision": 2,
            "expression": {
              "op": "divide",
              "args": [{ "op": "multiply", "args": [{ "field": "subtotal" }, { "field": "percentualFee" }] }, { "value": 100 }]
            }
          }
        },
        "total": {
          "kind": "currency",
          "computed": { "precision": 2, "expression": { "op": "add", "args": [{ "field": "subtotal" }, { "field": "valorFee" }] } }
        }
      },
      "validations": [
        {
          "name": "pagamento-limitado-ao-contratado",
          "code": "PAGAMENTO_ACIMA_CONTRATADO",
          "field": "valorPago",
          "message": "O valor pago não pode superar o valor contratado.",
          "assert": { "op": "lte", "args": [{ "field": "valorPago" }, { "field": "valorContratado" }] }
        }
      ]
    }
  ]
}

Estrutura

Propriedade Obrigatória Descrição
name sim Nome legível da declaração.
slug não Minúsculas, números e hífens.
schemaVersion sim Deve ser 1.
models sim Lista não vazia de models.

Model

Propriedade Obrigatória Descrição
name sim PascalCase, usado no registry e no Mongoose.
singular não Nome singular.
basePath não Caminho iniciado por /.
crud não Mesmo contrato de defineModel.
options não Opções JSON do schema Mongoose.
fields sim Objeto com pelo menos um campo. Tipos em Campos.
validations não Regras executadas depois dos cálculos.

Escopo da versão 1

Cobre: identidade, models e caminhos de API, CRUD, campos primitivos/enum/ref/moedas, obrigatoriedade, default, busca, unicidade e índice simples, limites numéricos e de texto, campos readonly protegidos, campos calculados com dependências ordenadas, precisão, tratamento de ausentes e validações entre campos — com todos os problemas retornados numa única exceção.

Ainda não cobre: índices compostos, triggers e transições (use o manifesto de processo), migrações automáticas, mappings de integração e funções JavaScript embutidas.

Proteção de campos somente leitura

A proteção não depende do frontend:

  • valor readonly enviado na criação é rejeitado;
  • alteração de valor readonly é rejeitada;
  • em edição, o mesmo valor pode voltar no payload e é removido antes da persistência;
  • campos calculados podem voltar no payload somente quando coincidem com o resultado do servidor;
  • tentativa de adulterar um campo calculado é rejeitada;
  • services recebem somente campos permitidos e os resultados recalculados.

Isso permite formulários que enviam o registro completo sem abrir espaço para alterar totais, status técnicos ou identificadores controlados pelo sistema.

Erros do manifesto

Um manifesto estruturalmente inválido lança DomainManifestError, agregando todos os problemas para corrigir em uma rodada:

{
  name: "DomainManifestError",
  code: "OON_DOMAIN_MANIFEST_INVALID",
  statusCode: 422,
  issues: [
    {
      path: "models[0].fields.total.computed.expression",
      message: "dependência circular entre campos calculados: total -> fee -> total."
    }
  ]
}

APIs públicas

Na operação normal você não precisa chamá-las: oonCore-back start descobre o manifesto automaticamente.

const {
  validateDomainManifest,
  domainManifestToDefinitions,
  registerDomainManifest,
  loadDomainManifest,
  evaluateDomainExpression,
  applyDomainMutation,
  validateDomainRecord,
  detectChangedFields,
  DomainManifestError,
  DomainRuleError,
  DOMAIN_EXPRESSION_OPERATORS,
  SUPPORTED_DOMAIN_FIELD_KINDS,
} = require("@oondemand/oon-core-back");

Migração gradual

A ordem de carga é central.config.js → central.domain.json → diretórios em src/. Migre model por model; defineValidation continua disponível e roda depois das fórmulas e validações declarativas.