N NezamDocumentation

Rules, tests, and errors#

Business Language has grammar-level support for executable rules, validation rule blocks, tests, and message-centered error handling.

Rules#

Rules are reusable executable declarations. A rule can have a full function-like body or be declared by name for implementation elsewhere.

bl
rule can_post_invoice(invoice: Invoice): bool {
  return invoice.status == "ready";
}

rule configured_elsewhere;

Executable rule declarations use the colon return form. Do not write rule name(...) -> Type; that is function syntax, not rule syntax.

Use rules for reusable logic. If a rule depends on configurable thresholds, routes, statuses, outcomes, or reason codes, resolve those values from configuration tables.

Validation rule blocks#

Standalone validation rule declarations support ensure, check, and require statements. In this form, ensure requires message, check requires on, and require requires else.

bl
validation rule RequiredInvoiceAmount {
  ensure amount is not null message "Amount is required";
  check amount > 0 on amount;
  require currency_code is not null else "Currency is required";
}

Named validation declarations can group validation statements and validation functions. In this form, message, on, and else trailers are optional.

bl
validation InvoiceValidation {
  ensure amount > 0 message "Amount must be positive";
  check vendor_id is not null on vendor_id;
  require status is not null else "Status is required";

  validate can_post(invoice: Invoice): bool {
    return invoice.status == "ready";
  }
}

Validation functions also accept -> return markers and trailing parameter commas.

bl
validation InvoiceValidation {
  validate normalize(input: string, fallback: string = "n/a",) -> string {
    return input ?? fallback;
  }
}

Tests#

Tests are declaration blocks with context values, assertions, expectations, setup, and teardown.

For the full syntax reference, see Test declarations.

bl
test InvoicePostingTest {
  context invoice: Invoice = sample_invoice;

  setup {
    seed_posting_policy();
  }

  assert invoice.amount > 0;
  expect can_post_invoice(invoice) to be true;
  expect post_invoice(invoice) to throw PostingError;

  teardown {
    clear_posting_policy();
  }
}

Supported expectation matchers are:

MatcherShape
Throwexpect expr to throw Type
Beexpect expr to be value
Equalexpect expr to equal value
Containexpect expr to contain value

Throw matchers accept full type expressions, including qualified names.

bl
test QualifiedFailure {
  expect post_invoice(invoice) to throw errors.PostingError;
}

Error declarations#

The minimal error declaration form declares a named error.

bl
error PostingError;

User-facing failures should prefer declared messages with stable codes, severity, category, and localized text.

Try, catch, and finally#

bl
try {
  post_invoice(invoice);
} catch (err: PostingError) {
  raise message posting_failed(invoice.invoice_id);
} finally {
  release_posting_lock(invoice.invoice_id);
}

Additional catch clauses in normal statements use typed parameters.

bl
try {
  post_invoice(invoice);
} catch (err: PostingError) {
  raise message unexpected_posting_error();
}

Message-backed raises#

Use message-backed raises for user-facing failures.

bl
raise message posting_policy_required(company_code_param) with {
  company_code: company_code_param;
};

raise posting_policy_required;
raise posting_policy_required(company_code_param);
throw message posting_policy_required(company_code_param);
throw error_value;

The raise grammar supports message Name(args), message(args), or Name(args), with optional context entries after with. Message-backed raises can include arguments or omit them. Use throw <expression>; for non-message expression throws.

Closed failure behavior#

Missing required configuration should raise a declared message-backed error. Do not continue with fallback behavior for missing policies, unsupported modes, unknown actions, or invalid enum-like values.

Source: packages/business/language/rules-tests-errors.md