Breaking circular logic
Often, when modeling business logic, we encounter circular logic systems where A depends on B and B depends on A. Sometimes models have enough information to know what should happen in order to avoid infinite cycles and other such ambiguities, but most of the times that is not the case.
Let’s take a common case as an example:
We need to calculate the value of an item with Vat tax included, but, to make the sale easier, the item needs to be sold at a rounded value and then the raw value is the one that needs to be calculated. Most developers will approach this problem with an interface that looks like this:
If the user changes the Vat free value (Vfv) the app will calculate the Vat included value (Viv) and vice versa. And everything seems to work fine at first, but a more robust test will quickly show that this is a broken system.
The problem is, now you don’t know what defined the price. Was the item price defined in vat free and we rounded off the vat included price, or the other way around?
Depending on the business you are trying to implement that might be important.
A second problem that might arise is that these formulas might not be injective, meaning that you cannot derive the inputs from the outputs. And if that is the case, then the calculated values are not going to be stable and predictable, and they will stop matching the original user input.
A third problem is that both values are user input, and both values are application calculated. If there is any kind of bug in the calculation, and you need to fix it, you have no way to distinguish which of the two values should be corrected.
This transition based model breaks most inference algorithms. It forces you to model not only the whole state machine but also what needs to happen in every transition. This is a simple case, but if you start applying this logic around every part of your model you end up worse off than if you were coding everything by hand.
So, what is the solution for declarative, idempotent and behavior inference modeling frameworks like Genio?
You break the cycle. You make explicit the intention of the user and switch your interface to ask for a reference value and for the interpretation of that reference value. Then you are free to do your non-circular calculations to make the rest of your business logic happy.
The interface should look something like this:
The calculation formulas will be slightly different to take into account the type of value (Rt):
Viv = (Rt=="VI") ? Rv : Rv * (1+Rate)
Vfv = (Rt=="VF") ? Rv : Rv / (1+Rate)
Where Rv is the reference value, Viv the Vat included and Vfv the Vat free. You make the option explicit to the user and use his choice to interpret the reference value correctly. From the Viv perspective if you interpret the reference value as 'Vat included' then you use Rv directly, otherwise you convert it using the Rate. And for Vfv you just do the opposite.
This way you know exactly how each value was created, by the user or by the system. You can persist all the information about that record, and more importantly any correction that you do won’t have to touch any user inputted data. And when things go wrong, trust me, messing with user data is the last thing you want on your task list. Plus now you are free to evolve the system in new ways like providing a default for the option if forms start vat free on not.
The cost of those two extra fields will pay for itself over the years tenfold, in ease of maintenance, in resilience to change, and most of all, in potential failure points you sidestep.