Rule Development Guidelines & Recommended Practices
Author:
Fluent Commerce
Changed on:
1 July 2024
Overview
This page provides an overview of the guidelines and best practices for custom rule development.
It is highly recommended that you first read Designing Rules and Writing Custom Rules if you have not done so already.
Key points
- This document will cover the following topics:
- Best Practices
- Rule Naming Conventions
- Rule Descriptions
- Rule Parameters
- Exception Handling
- Logging & Audit Events
- Utilities, Helpers, and Services
- Constants
- Documentation
- Additional Best Practices
Best Practices
- SRP (Single Responsibility Principle) - The rule should do one thing, and one thing only
- Atomic, independent & re-usable - A rule should be the smallest unit of logic, as isolated and reusable as possible
- Avoid "arrow-code" - If your rule begins to have multiple and nested if statements, it could be an indication to split up the logic.
- Exit or fail early - Use guards - helps to avoid arrow-code.
- Use the Plugin SDK Utils - Avoid writing custom validation or util logic if provided by the SDK Utils. If you need some additional utilities, ensure you abstract away those utilities into your own reusable util class within your plugin project.
- Avoid implementation specific details where possible
- Only fetch what is needed, and when it is needed
- A rule should only produce 1 action
- A rule should not check state
- A rule should only throw Exceptions of type , or sub-class thereof
`RuleExecutionExeception`
- Try to keep rules to only a few lines of code, but avoid locking potentially reusable code into private methods - extract into shareable classes like Utils, Services, etc.
- Avoid extraneous null checks and writing potentially null returning code
- Avoid "magic strings"
- Attach process data (the result of rules) to the entity - for example, if a rule has performed some condition to determine a meaningful result, which could be relevant to future rules/rulesets, attach the data to the entity via an attribute.
Rule Naming Conventions
Since there will be lots of Rules registered in your Account, it is highly recommended to name rules in such a way that they are both easily read and understood. The more descriptive and meaningful the name, the more efficient designing and configuring workflows will become.
Remember, Rules should be as atomic as possible. They should not perform multiple actions, or overly complex logic, but rather be orchestrated into a process that fulfills and executes complex logic and multiple actions.
To this end, your rules should be simple to name, since they should be doing something simple.
The rule name needs to reflect a verb and needs to be as descriptive as possible.
- The Rule Name should indicate the Action
- The Rule Name should indicate the Entity or Domain to which it applies
- The Rule Name should be descriptive of the behaviour or logic it uses
Some examples include:
- : Sends information back to a client via a Web hook
`SendWebhook`
- : Schedules an Event
`ScheduleEvent`
- : Creates a new entity of type
`Create<Entity>`
- : Updates an existing entity of type
`Update<Entity>`
- : Cancels a specific entity of type
`Cancel<Entity>`
- : Adds an Attribute to an entity of type
`AddAttributeTo<Entity>`
Example:
`SendEventToOrderWithAttribute`
Rule Descriptions
Rule descriptions need to describe in detail what the building block does. They should be written as a single sentence and use parameters wherever possible.
- The Rule Description should clearly describe exactly what the Rule does
- The Rule Description should include the Rule Parameters as per curly bracket syntax
- The Parameter Names should come from Constants to avoid the magic string anti-pattern
Example:
`Send Event {eventName} to Order with Attribute Name {attrName} and Attribute Value {attrValue}`
Rule Descriptions and the Modeller
It is important that all parameters defined for the rule via the
`@Param<Type>`
The syntax here is important. Wrap parameter names in curly braces
`{`
`}`
1 @RuleInfo(
2 name = "ExampleRule"
3 , description = "This is the description of the Example Rule using parameter {" + MyRuleConstants.PARAM_STRING_1_NAME + "}"
4 )
5 @ParamString(name = MyRuleConstants.PARAM_STRING_1_NAME, description = "Parameter String 1", defaultValue = "PS1")
6 public class ExampleRule implements Rule {
7
8 // ...
9 }
Language: json
Name: Code sample
Description:
[Warning: empty required content area]This will render a form field for use within the Modeller:

Rule Parameters
Input parameter names need to be defined as static variables, and used within the Rule Description, Param Annotations, and accessors.
For example, the Rule Description should be:
`"Send Event {" + ParamConstants.PARAM_EVENT_NAME + "} to Order with Attribute Name {" + ParamConstants.PARAM_ATTR_NAME + "} and Attribute Value {" + ParamConstants.PARAM_ATTR_VALUE + "}"`
The Parameter Annotations would be:
`@ParamString(name = ParamConstants.PARAM_EVENT_NAME, description = "The name of the Event to Send")`
@ParamString(name = ParamConstants.PARAM_ATTR_NAME, description = "The name of the Attribute to attach to the event")
@ParamString(name = ParamConstants.PARAM_ATTR_VALUE, description = "The value of the Attribute to attach to the event")
Exception Handling
The Rubix Orchestration Engine ensures that all Exceptions thrown out of Rules are recorded within the Orchestration Audit Events. This is important for providing detailed information about what went happened during an event execution.
Typically, we recommend the following practices:
- Do not use try-catch blocks, rather allow exceptions to bubble up to Rubix
- If catching any exceptions within a rule, ensure the cause exception is either re-thrown or included as a cause throwable in a new exception
- If throwing a new exception from within a rule, ensure you capture as much information as possible within the exception, so that the Audit Events provide rich and useful information
- Do not swallow exceptions
See Exception Management for more information on exceptions.
Logging & Audit Events
The Rubix Orchestration Engine is designed to provide Audit Log Events during the execution of any workflow.
This includes the following categories of Audit Events:
- Snapshot - A snapshot of the Entity at the time the Orchestration Event is received into Rubix
- Ruleset - An audit of the ruleset executed as a result of an Orchestration Event
- Rule - An audit of a Rule executed within a ruleset as a result of an Orchestration Event
- Action - An audit of an Action executed as a result of an output of a Rule
- Exception - An audit of an Exception that has occurred during the execution of an Orchestration Event
Sample Audit Event
1{
2 "id": "94b1f320-d7b3-11eb-af98-5d4bd96e41b5",
3 "name": "MyRuleset",
4 "type": "ORCHESTRATION_AUDIT",
5 "accountId": "Demo1",
6 "retailerId": "1",
7 "category": "ruleSet",
8 "context": {
9 "sourceEvents": [
10 "94901340-d7b3-11eb-af98-5d4bd96e41b5"
11 ],
12 "entityType": "ORDER",
13 "entityId": "90",
14 "entityRef": "order74",
15 "rootEntityType": "ORDER",
16 "rootEntityId": "90",
17 "rootEntityRef": "order74"
18 },
19 "eventStatus": "NO_MATCH",
20 "attributes": [
21 {
22 "name": "startTimer",
23 "value": 1624845185818,
24 "type": "STRING"
25 },
26 {
27 "name": "stopTimer",
28 "value": 1624845185818,
29 "type": "STRING"
30 }
31 ],
32 "source": null,
33 "generatedBy": "Rubix_API",
34 "generatedOn": "2021-06-28T01:53:05.818+0000"
35}
Language: json
Name: Code sample
Description:
[Warning: empty required content area]LogAction
In addition to the default Audit Events generated by the platform, a special action is provided as part of the Rubix framework. This action allows Rule writers to log additional information to the Orchestration Audit events log. This is done using a
`LogAction`
See LogAction for more details on best practices to using the
`LogAction`
Utilities, Helpers, and Services
Use of static utility methods in a purpose-built Util class should be used appropriately. Keep in mind the unit testability of your code base, and avoid the use of unnecessary static methods and classes where possible.
Helper or Service classes such as
`RetailerSettingsHelper.java`
`OrderService.java`
Constants
Any client specific constants such as specific states and event names should be in a constant class as part of the client plugin.
Documentation
Rules should be self-documenting. The
`@RuleInfo`
`javadoc`
The
`@EventAttribute`
`@ParamString`
Additional Best Practices
The following are some best practices to consider when writing custom rules.
Latest Dependencies
Use the latest dependencies of the API client and Rubix SDK. Once a plugin is deployed it will always pick up the latest libraries and ensure backward compatibility.
Avoid state checks
A rule should never check the state of an entity. This is the responsibility of the workflow and the ruleset trigger. For example:
`if fulfilment.getstatus() == FULFILLED`
Entity Attributes
Use attributes on entities to flag them for further checks. For example:
- order has been fully paid
- is kiosk order
- fraud check complete
- expiry added
Retailer Settings
Retailer settings should be used to store any kind of static properties (not parameters) which need to be accessible within a rule such as API keys and webhook endpoints.