Skip to content

Managing Rules

valhuber edited this page Jan 1, 2021 · 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)

"Think Spreadsheet" - 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. It's a higher level of abstraction, where you focus on what, not how:

Rules are like spreadsheet formulas:
automatically reactive to changes in referenced values
automatically chained

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.

Or, review the Rule-Summary.

Database Design

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

Model files: consider sqlacodegen, provide empty constructor

SQLAlchemy depends on model files. If you have an existing database, as described here.

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. Many of the examples use stored aggregates, but it's a choice: you can alter the stored / non-stored design without affecting your logic or your apps; 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 examples/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)

Relationships: Referential Integrity

Enforce Referential Integrity (RI) with support from the DBMS, SQLAlchemy and Logic Bank. For more information, see Referential Integrity.

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.

Single-field constraints

While most of the examples focus multi-field / multi-table, it's a great idea to start simple with single-field constraints. And don't forget - constraints invoke standard Python, so you're fully enabled to utilize all the existing libraries for credit checks, etc.

Referential Integrity

Another piece of low-hanging fruit is referential integrity. If you are unable to enforce it in your database (e.g., no schema access), you can use Logic Bank - check out Referential Integrity Support.

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.