Business Rules

Validating Business Rules with Schematron

These rules span the document: the record count compares header and body, the year check reads the header from every book, which an XSD 1.1 assertion cannot (Assertions and CTA), and sort names are computed from names. A feed with a wrong count, a book dated next year, an uninverted sort name and a reused SKU passes the RELAX NG grammar:

Running the compiled rules on a feed with four business-rule faults
sed -e 's#<records>6#<records>7#' -e 's#<published>2026#<published>2027#' \
    -e 's#<sort-name>Reyes, Tomas#<sort-name>Tomas Reyes#' -e 's#>BN-0005<#>BN-0001<#' \
    booknest-catalog.xml > rules-feed.xml
jing booknest.rng rules-feed.xml && echo "grammar: valid"
xslt3 -xsl:booknest-sch.xsl -s:rules-feed.xml -o:rules-report.xml
xquery -s:rules-report.xml -qs:'//*:failed-assert ! string-join((@location,
  normalize-space(*:text)), " ")'
Output
grammar: valid
"/Q{}catalog[1] The header promises 7 books, found 6."
"/Q{}catalog[1] A book is published after 2026, the year the feed was sent."
"/Q{}catalog[1]/Q{}book[2]/Q{}contributor[1] Sort name Tomas Reyes should be Reyes, Tomas."
"/Q{}catalog[1]/Q{}book[5] Identifier BN-0001 is already used by an earlier book."

Run the grammar first, then the compiled rules, and fail the feed on any failed-assert without role="warning"; the path() locations point the quarantine report at each record. The compiled stylesheet is plain XSLT that Saxon 726,956 , SaxonJS 726,956 (XSLT in JavaScript) or a CI job can run.