Skip to content

Managing Rules

valhuber edited this page Dec 3, 2020 · 15 revisions

Rules are Declarative. It's important to review that link, and consider the guidelines on this page.

Don't

It's well to start off by remembering things you don't do with rules:

  • Order - unlike code where order is critical, rule execution is via system-discovered dependencies. When you change the logic, the execution order changes accordingly. This means you just add rules without worrying about ordering.
  • Call - you don't call the rules, the rule engine does. This means your logic always runs.
  • Optimize - SQLs are pruned and optimized for you (e.g., adjustments)

Favor Rules Over Code

New users will recognize Event rules immediately - they are a familiar metaphor (triggers etc). And you will certainly use them.

But rules are much more efficient of your time, so it pays - a lot - to spend the design time to search for a rule solution before you jump into code.

State the Requirement

Begin by stating the requirement, e.g., Check Credit. Then, define rules that implement the requirement.

Rule Diagrams

Rules are about the data, so it's useful to superimpose the rules on a database diagram:

Rule Discovery

The simplest way to discover rules is using your IDE, as described in the Logic Bank Tutorial.

Database Design

Rules depend on database design, so these concepts below are crucial.

Normalized

Normalize your database design as usual.

Performance Denormalizations

That said, you can get sometimes-enormous performance gains by introducing performance denormalizations for sum / count aggregates. See Denormalizations.

Relationships: DB, or Virtual

The interesting rules (sums, counts, parent references) depend on relationships. It's great to have these in the database, but if that's not possible, you can still define them in SQLAlchemy:

    # from nw/db/models.py, for Customer...
    OrderList = relationship("Order",
                             backref="Customer",
                             cascade="all, delete",
                             passive_deletes=True,  # means database RI will do the deleting
                             cascade_backrefs=True)

Columns as Rows does not work

A typical approach for extensibility is to define columns as rows, e.g., each column is a child row:

primary-key | col-name | value

Such columns are not visible to SQLAlchemy, or Logic Bank.

Rule Patterns

Rules are a different paradigm than code, and there are important patterns in their use.

Constrain a derived result

You often define aggregates to test them in constraints. You may even do it when the aggregate value is not of direct business interest. The Check Credit illustrates this pattern (Customer.Balance) is the derived result.

Counts as existence checks

Counts are useful since 0 means there are no children. See Orders for Commissioned Employees Only.

State Transition Logic (old values)

The oldRow is provided so you can see whether (or how much) a value changed. See our favorite example: Sufficient Raise.

Debug

Logic includes Python, not just Rules. You can set breakpoints and step in Python code, as usual.

Rule Logging

Rule Logging can reduce the need for tedious debug sessions.