N NezamDocumentation

Configuration-driven ERP#

Business policy is configurable when it can vary by company, country, process, tenant, or date. Configurable policy belongs in .fields.bl and .tables.bl, then gets resolved by .functions.bl or .services.bl.

Required pattern#

  1. Declare typed fields for identifiers and codes.
  2. Store policy values in configuration tables.
  3. Resolve exactly one active configuration row for the current scope.
  4. Fail closed with a declared message when required configuration is missing or ambiguous.
  5. Cover success and failure paths with e2e tests.

What not to do#

Do not encode variable policy as string-literal branches:

text
if (company_code_param == "SA01") {
  return "manager";
}

That behavior should be represented as data:

bl
field CompanyCode: string {
    max_length: 10;
}

field ApprovalProcessCode: string {
    max_length: 40;
}

field ApprovalRoleCode: string {
    max_length: 80;
}

table ApprovalRoutePolicy {
    field company_code: CompanyCode key;
    field process_code: ApprovalProcessCode key;
    field approver_role_code: ApprovalRoleCode;
    field active: bool required default(true);
}

Failure behavior#

Missing required configuration is not a defaultable case. Raise or return a declared message-backed error so callers can report the cause.

bl
message approval_route_policy_required {
    code: "WF-ERR-1001";
    severity: error;
    category: custom("config");
    params: {
        company_code: CompanyCode;
        process_code: ApprovalProcessCode;
    };
    message: {
        en: "Approval route policy is required";
    };
}

Invariants#

Use enums only for values that should not vary by tenant or process. Debit and credit indicators are stable invariants. Approval states, routing outcomes, thresholds, and reason codes are usually policy data.

Source: packages/business/docs/guides/configuration-driven-erp.md