Published: Invalid Date
10 min read
#React
Small composable rules enable the composition of business logic. This is important to maintain flexibility and minimize the cost of change over time. These are 2 of the most common reasons our customers choose Fluent Order Management!
So why is this so important? In this first article in the “Composable Rules Series”, I present “the why”. We’ll explore 7 reasons for building small composable rules.
By the end of this article, you will understand the benefits for both developers and our customers.
In short, the 7 reasons (and I am sure there are more) to build and use small composable rules are:
Let’s delve into each one of these reasons to unpack the value.
This might seem obvious, but the smaller the code unit, the quicker it is to write and test it. Now, don’t get me wrong, in certain areas of software development, even a few lines of code can represent what some might consider “rocket science”. These may be the result of significant research, testing, and time. I’m talking about advanced algorithms and lower-level computer science problems.
While there are times when a more advanced rule may be required, this is not the majority of use cases in the order management domain. In general, every requirement can be broken down into small steps. The power of the workflow engine is its ability to execute each step of the process efficiently to solve for the requirement.
The more code paths in a single rule, the more complex it is to test and debug. More tests are required to cover all code paths.
So, smaller pieces of code are quicker and easier to write and test. Subsequently, each rule then has one very clear and concise job to be done. It has fewer code paths to assert for, and therefore fewer opportunities for fragility.
Smaller Rules are also easier to name - they do one very small specific thing. Rule names should be as intuitive as possible. Ideally, you should not need to read the Rule description (or worse, the code) to understand what it does.
Consider the following 2 examples:
The first one is far more meaningful and self-explanatory. It clearly performs one simple job: forward the process to a specific next step based on whether the delivery address of the order is present.
It is clearly a Rule that is intended to be composed into a logical gate Ruleset. It does not do anything if the delivery address is not present. That is not its job. It’s only job is to forward the flow to the next task if the Rule criteria is met, otherwise it does nothing.
This Rule is simple, self-explanatory, and much easier to test since it only has 2 code paths.
The second one is not so meaningful. What does it mean to be “verifying fulfillment items”? Why is quantity not a number? Does this rule handle all 3 “quantities” and send 1 of 3 different events?
Looking at the Rule parameters, one can determine that it will send a single event based on a single “quantity” matching criteria. So, similar to the first Rule, it is a flow control Rule with criteria. In this case, the matching criteria value is configurable, while in the first Rule, it is specific to the entity data. It is in fact a composable Rule, but one has to look much deeper to understand what it is doing.
The Fluent OMX Workflow Engine logs an Orchestration Audit Event for each Rule, Action, and Ruleset. This means that when your Rules are more granular, so will your audit event logs.
This makes a significant difference when identifying issues with certain orders or processing.
With large monolithic rules, debugging becomes complex as these code paths are not reflected in the standard Audit Log Events. It essentially becomes a black box, where the inner workings are hidden.
When an exception might occur in the middle of one of the many code paths in the rule, the Audit Event will only present is the name of the rule with the exception.
If there were only 1 or 2 code paths in the rule, it would be much quicker and easier to identify why it failed, and resolve the issue.
Since smaller rules have more meaningful names, it also makes it easier for non-developers to understand. It may even help them identify and resolve the issue without the help of a technical person.
Rules are the building blocks for business logic, not the business logic itself. Rules are composed together into Rulesets to form units of logic, such as a task. Rulesets fulfill a function or process.
Configuration of business logic happens at multiple levels:
Importantly, the goal is not to avoid writing code. The goal is to write Rule code once.
When a new requirement comes in, there is no need to change existing Rule code. Rather, new Rules or Rulesets could be added, removed or reordered. Or, new code is written as a new Rule to compose together with the existing Rules.
To illustrate this, let’s look at the following example:
Your client wishes to implement a fraud check as part of the Order Booking process. Initially, they ask for any Orders over $100 to be flagged for a fraud check, and not continue the Order Booking process.
This requirement could be implemented via configuration of Rules from the Core Reference Module as follows:
A Fraud Check configured by composition of Core Reference Module Rules
Later, based on analysis of their data, they realize most Orders over $100 are okay from existing customers, so they ask for a change to the fraud checking logic. They ask that only Orders over $100 for customers with less than 2 successfully completed Orders in the past 6 months get flagged for a fraud check.
To implement this logic, a new Rule would be required to query Orders for the current Customer reference where the Order status is COMPLETE.
There is no need to change the existing Core Rules code. Rather one or more new Rules could be written and configured within the workflow like so:
Fraud Check Logic composed of Core Rules and New Rules
Perhaps later, the client realizes that while the customer may have more than 2 completed orders, they also return a lot. In this case, they would like to add another condition to the fraud check. This time, they ask that any Orders over $100, where the customer has more than 1 return and less than 5 completed orders gets flagged for fraud check. Again, only the new Rules are written and then configured into the workflow:
Fraud Check Logic composed of Core Rules and New Rules
Smaller composable rules mean more unique solutions are possible with the same set of building blocks.
Large monolithic rules are much more difficult to adapt and change, meaning they are more costly.
Fluent Commerce customers need the ability to adapt to their market continuously. The Fluent Platform is designed to empower customers to do this without significant development costs each time they want to change or extend their business logic.
Smaller composable rules tend to be more versatile. They can be combined with a number of other rules to produce unique functions or tasks within a process. The Fraud Check example in the previous point demonstrates this well. The same Rules are used multiple time with different parameters.
This reusability prevents the need to write specific rules each time the function or task is required. This reduces the total cost of ownership, and continuously increases the return on investment.
For example, the Core Reference Module provides a number of Rules that are flexible enough to be used on any orchestration entity and perform various actions, such as setting state or sending events.
Another example could be a Rule like “SetAttributeIfPropertyEquals”. This Rule could set the value of an Attribute on the current entity if a property (configured by JSON Path) equals a provided conditional value.
This rule could be used to set attributes on any entity, making it reusable across domains. Like many of the Core Rules, the JSON Path could be configured to compare against an entity field or an event field.
This helps prevent duplication of code, reducing maintenance costs, which further improves TCO and ROI.
There are 2 aspects to how small composable rules decrease extension cost:
It is key to maintain the same principals when implementing new rules. New rules should also be small composable building blocks. This further contributes to the value propositions discussed above, maximizing the potential ROI.
As a developer, you are paid to deliver solutions to problems. Solutions that hold intrinsic value to someone or something. The more value you can deliver, the more valuable you are.
The opportunities for delivering more value as a developer are simple:
By writing and using small composable rules, you reduce the time spent on the basic fundamentals of an order management workflow. You get to spend more time designing and building solutions for the unique, customer specific problems. This is what holds high value to that customer.
For example, let’s assume the basic fundamentals of implementing order management include:
In reality, each of our customers will have more specific requirements or nuances to these fundamentals. There are many more complexities to identifying the best fulfillment location. It might not always be the location closest to the customer.
Let’s explore a few examples…
A high end luxury retailer may have caveats to the requirements, such as
They may not make as many sales as a mainstream retailer, but their margins are likely much higher per sale. This means that fulfilling an eCommerce order from the closest store location may not be in their best interest, even if it is the cheapest way.
Protecting the stock levels and in store experiences holds higher value to them than the logistics spend of shipping from a different location.
Taking another example, an appliance retailer has a different set of challenges since many of the products they sell are big, bulky, and heavy.
They are not typically fulfilled through BOPIS (buy online, pick up in-store) since most consumers can’t put a full-size fridge in their car.
They also typically offer installation and old appliance removal services in addition to the physical product sale. This means delivery times and scheduling are very important, as is optimized routing for delivery trucks mapping out the day's jobs.
As a final example, imagine you are a trades-person or handyman. You’re working a job in the suburbs and realize you are missing an important part, perhaps a very specific door hinge. Rather than wasting time driving around to each of the hardware stores in the area to find out whether they have the correct hinge in the correct color and in the right size, you grab your phone and look online.
Some of the hardware stores in the area don’t provide clear availability information, while others do. You find the part you’re after and can immediately buy it online for pickup within 30 minutes. You drive down to the store to collect the part, and while you’re there, you see a massive sale on various handyman consumables, such as masking tape, painting supplies, and various sizes of nuts and bolts.
Knowing you always need them, you grab a few rolls of tape and a couple of new paint brushes, rollers, and paint trays. You checkout and return to your job site satisfied.
This hardware retailer provided the best-in-class online availability and pickup fulfillment options compared to its competitors, and increased their ACV (average customer value) by getting you in store, and selling you more by knowing their customer and their needs.
Additionally, chances are that next time you’re in a similar position, you might start your search with this hardware retailer over others.
These are just some examples of how different customers have different priorities, needs, or business objectives in their order management requirements.
This is where the value of our developer community comes in. Get to know your customer, understand what is most important to their bottom line or their competitive advantage, and help deliver solutions to those specific needs.
Each of the examples above will likely need some well thought through solutions that require advanced workflows and new rules.
Delivering the fundamental functionality is one thing. Optimizing for the business outcome is another.
Get the fundamentals done quickly by composing logic in workflow, and then focus your time on the high value outcomes.
The benefits of building and using small composable rules are clear (I hope!). But sometimes, it is harder to break something down into smaller reusable parts.
It can feel quicker to just write the solution in code. And this gives a greater sense of reward and satisfaction when “it works”. Yet in the long term, there are costs or pain that may result from this approach.
Later, when you’re trying to debug an intermittent problem, you realize you can’t easily trace the code path taken. Perhaps you didn’t think to write a test case for this scenario at the time, and it takes you days to find it!
That's the pain and the cost.
What’s more, once you fix this problem, you may find you have broken something else in the process, and it starts to snowball.
So, in conclusion, smaller composable rules:
They also deliver more value to our customers:
It’s a no-brainer.
Lesley Dean
Helping developers deliver high value real-world outcomes for our customers
Disclaimer: All information and resources found on community.fluentcommerce.com are based on the opinions of the author (unless otherwise noted). All information is intended to inspire and motivate well thought through technical decisions inline with Fluent Commerce recommended practices and principles.
Copyright © 2025 Fluent Commerce