Designing Rules
Author:
Fluent Commerce
Changed on:
19 Apr 2024
Overview
This page provides all the information needed to design best practice Rules for your workflows. There are some important things to be aware of, so we highly recommend you thoroughly read through this page, and refer back to it regularly during development.
Key points
- providing a guideline of designing your Rule within Fluent
- It is important to keep the best practices and design principles in mind when designing your rules.
- understand types of analogies when come to building rules
Pre-requisites
We recommend that you first read the following pages prior to this page:
- Make sure you've read and understood the Rubix Orchestration section.
- Understand the flow, and especially how Events and Triggers work.
- Read about Designing Workflows and Understanding Event Execution.
- Read the Anatomy of a Rubix Rule to understand the Rubix Rule class structure and configuration.
Designing your Rule
It is important to think carefully about your Rule design. Most of the time, you will be thinking about the rule in the context of a specific workflow and use case. It is recommended to step back and assess how this requirement can:
- be simplified
- be generic
- be flexible
- be re-usable
- be part of multiple compositions
Best Practices and Design Principles
It is important to keep the best practices and design principles in mind when designing your rules.
Use the Development Guidelines & Best Practices documentation as a checklist and within the code review process to validate recommended practice.
As a guideline, most rules should hold the following structure:
1@RuleInfo()
2public class DemoAddAttributeToOrder implements Rule {
3
4
5 @Override
6 public void run(Context context) {
7
8 // validate inputs (RuleUtils)
9
10 // if required, get detailed entity info (only if not available on the context.getEntity() already)
11
12 // simple condition
13
14 // simple logic (construct new mutation or event object for example - this should often be abstracted away from the rule class too)
15
16 // call action (context.action())
17
18 }
19}
Language: java
Name: Sample code
Description:
[Warning: empty required content area]`
`
The Lego Brick Analogy

We've previously used the analogy of a Rule being like a Lego block. Each Lego block comes in different shapes and sizes but can be used in a variety of scenarios, and to deliver multiple different results.

By composing multiple different Lego blocks together, you create different structures. Similarly, composing multiple different rules together into rulesets produces different logic and behavior.
The Music Analogy

Another analogy might be music. The Orchestration is the Orchestra. The Entities are the Instruments. The Workflows are the Melody. The Rulesets are the Chords. The Rules are the Notes.
Importantly, there are a finite number of notes, and yet depending on how they are composed into Chords and Melodies, they produce an endless amount of unique sounds and compositions of music.
Rules should be like music Notes.
Rulesets and Workflows should not find themselves having to create their own unique Rules more often than not. They should simply be orchestrating a composition of Rules and Rulesets to produce their unique melody... their unique process.
By simply adding, moving, or removing a single note (rule) from a single chord (ruleset), the whole melody (workflow) changes.
The Programming Analogy
One last analogy is in fact programming. Now, it's important to make this comparison with an objected oriented view, and a good understanding of design patterns and clean code principles. If you're a more procedural programmer, this analogy might not deliver the intended message.
Since Rulesets execute each and every Rule in the Ruleset in sequence, unless otherwise exited via an exception, a Ruleset is comparable to a method.
Each line of code within a method is executed in sequence from the top down unless there is an exit via a return or exception. This means Rules are more synonymous with lines of code, however, we could rather say blocks of code within a single method.
Just as there are recommended practices guiding clean code principles in object-oriented programming, so there are similar principles to designing Rules.
Now, in order to compose the logic or behavior of a method, typically, it involves adding, moving, or removing different lines of code around within to get the desired result.
Similarly, Rules should be the smallest possible unit of logic that can be added, moved, or removed from Rulesets to achieve the desired behavior and result.
A Rule Design Scenario
To demonstrate the composition of orchestrations, let's look at the following scenario:
An order comes in, and we want to perform validation on the Order prior to booking. Our requirement states that:
- The Order must be fully paid
- The Order must be from an existing Customer
- The Order must be less than $1000
Let's assume that if any of the above conditions are not met, the order cannot be booked and should rather go to a manual fraud check state.
There are 2 options for designing a solution to this within the workflow:
Option 1: Write a single rule, and have the rule itself evaluate each of the requirements prior to executing a change state rule to book the Order.

Option 2: Write 3 rules, each one dealing with a single requirement, prior to executing a change state rule to book the order.
Which one would you pick?
Let's discuss Option 1 first:
You decide it can all be written into a single rule, so to save time, you decide to go this route. You successfully deliver the rule, it works as designed, and only allows the order booking to take place if all of the above required criteria resolved true.
Your client is happy, and you successfully deploy this rule as part of the order workflow - your job is done.
A few weeks or months later, the client comes to you and says:
"Hey, Ms. Developer, the business would like to make a change to the Order Booking validation logic. Since this is Fluent Orchestration, we thought we could simply have our Business Analyst go in and make the updates, but they've said they can't do it! All we want to do is remove the part where it must be an existing customer. We don't want that logic anymore, and thought we could simply go in and change this in the workflow without incurring development costs?"
Or, perhaps they say:
"Hey Ms. Developer, we'd like to change the logic so that if it is a new customer, then the order should be under $1000, but if it is an existing customer, there's no limit..."
With this option, both of the requests for change by the client cannot be achieved code free. The only way to change the behavior in this option is to go back to the developer and get them to make the changes in the code.
Another thing to consider with this approach is that it is highly unlikely that this Rule, which is doing multiple things, is re-usable in any other place since it's too specific.
Let's now look at Option 2:
You decide you will create 4 rules. One for each requirement. Each rule will be composed sequentially into a Ruleset to be triggered on Order Creation. You code them up and deliver them to the client. The client is happy - your job is done.
A few weeks or months later, the client comes to you and says:
"Hey Ms. Developer, the business would like to make a change to the Order Booking validation logic. Since this is Fluent Orchestration, we thought we do this ourselves. All we want to do is remove the part where it must be an existing customer. We don't want that logic anymore, and thought we could simply go in and change this in the workflow without incurring development costs?"
This time, the change is simple, straightforward, and code-free!
Or perhaps they say:
"Hey Mr. Developer, we'd like to change the logic so that if it is a new customer, then the order should be under $1000, but if it is an existing customer, there's no limit..."
This one is still a little more tricky and may depend heavily on how configurable you made the original rules.
Since branching of flow is performed by sending an event for a different Ruleset, one option is to split up the existing Ruleset so that we can control the flow better.
Instead of a single Ruleset containing your 4 Rules plus the Change State Rule, we will now need to split this up to cater to the split conditional logic, and send an event to trigger the appropriate Rulesets conditionally.
The downside here is that each Ruleset essentially exposes an interface that can be triggered directly. In other words, an Event could be sent to trigger Ruleset B without first validating that Ruleset A had been executed.
The best way to ensure that Rulesets only execute or apply logic when they should is to tag the entity with data that essentially allows simple conditional guards to be put in place along the way. You can also make use of additional States to manage and guard the flow of logic appropriately.
For example, the following diagram shows an example of a much more granular approach to the requirements, delivering atomic, reusable, more compose-able rules:
