Designing Objects with Business Rule Checking
- Design Principles for Checking Business Rules
- Designing Business Rules Checking
- Property Write Accessors with Business Rule Checking
- Collaboration Accessors with Business Rule Checking
Business rule checking should be a fundamental part of every object's implementation, as essential as property accessors, object constructors, and equality operators. Making objects responsible for the rules that govern them is not only good object think, it is also good object design, ensuring robust business services and simplifying rule maintenance and upgrades. This article discusses design approaches for providing rule checking in an object's implementation. The material here is derived from the book, Streamlined Object Modeling, which includes Java and Squeak examples illustrating the design principles discussed here.
Design Principles for Checking Business Rules
In a previous article, "Enforcing Business Rules with Objects," we presented a business rules methodology for organizing business rules around objects. The three core principles of this methodology are the following:
Business rules keep a business process from causing harm, and are checked by the objects involved in the process.
Business rules are checked when the process causes its objects to change state.
Business rules are distributed among objects according to analysis patterns.
This article elaborates on the second principle by showing how to design rule checking. See the previous article for more on the other principles.
When to Check Business Rules
During the course of a business process, objects change state: order objects transition from pending, to final, to shipped states; account objects have their balances increased or decreased after deposits and withdrawals, respectively; and airplane objects receive assignments to flights on specific dates. In a well-designed system, the objects check the business rules before changing state, so that canceled orders don't get shipped, account balances don't go negative, and airplanes don't get conflicting assignments.
In a truly robust system, every change of state by an object is governed by business rules that the object checks before making the state change. Although such extensive rule checking may sound difficult to implement, the encapsulated nature of objects means there are really only three ways in which an object changes state: (1) by changing one of its property values, (2) by establishing a collaboration with another object, or (3) by removing an existing collaboration with another object. Actually, the last two ways are pretty much the same, so we can simplify a bit and say that the time to check business rules is when an object changes a property value or a collaboration.
When to Bypass Business Rules
Not all object state changes are the same. Restoring an object from persistent storage often means creating an empty object, and loading its properties and collaborations using stored values. Running business rules while re-creating a stored object is both inefficient and unnecessary. Similarly, certain business scenarios may require bypassing an object's business rules that are checked during other business scenarios. For example, an object may have a business rule preventing the removal of an essential collaboration, but may allow that collaboration to be switched out with another object. To make the switch, the affected object checks the new object against business rules, and if these succeed, bypasses the business rules for removing the existing collaboration. Bypassing business rules should be the exception, but any rule-checking design must be flexible to permit selectivity in rule checking.
Where to Check Business Rules
Putting rules directly within an object's class implementation limits system pluggability and extensibility. Instead, rule logic is typically delegated to policy objects, or housed in external rule databases or method dictionaries that can be updated frequently. A good design for rule checking is flexible enough to support all these implementations. The important thing is that the object affected by the rule initiates the rule logic, either by invoking its own internal services, by messaging a helper policy object, or by invoking methods stored externally, or by any number of other possibilities.
Logic versus Business Rules
Not all state transition rules are business rules. Business rules, which come from clients and domain experts, check property values and collaborating objects against acceptable ranges and standards defined within the business domain. Business rules specify that an account balance cannot be negative, a cancelled order cannot collaborate with a shipment, and an airplane must check any new flight assignment against existing flight assignments. Logic rules, on the other hand, check for invalid program states, such as passing a null pointer instead of an object, or a character value instead of a numeric value. Unlike business rules, logic rules typically don't evolve over time and don't get overridden by a specialization class, so it is a good idea to segregate logic from business rules.
In summary, there are five design principles for checking business rule in objects:
Business rule checking occurs when objects change state.
Business rule checking is invoked by the object changing state.
Business rule checking invokes logic that may or may not reside in the affected object's implementation.
Business rule checking must be flexible enough to allow selective bypassing by trusted services.
Business rule checking is separated from logic rule checking.