C
C#2y ago
LazyGuard

❔ How to design this code ?

Hi folks, I am having some troubles coming up with a design for the following problem. I have a Product object that have a several properties (e.g. category (a string), dangerous (a boolean), type (enum), length(int), height(int), weight(int) etc.) There is a set of rule that allows to set the value of some properties based on conditions on other properties. For example : Set the Type property to "Big" when Length >2 and Weight >15. Another example : "Set the Dangerous to true when Category == fireworks. Those rule must not be harcoded in the code, because our users want to have the possibility to add such rules on their own. Does anyone have an idea on how to do this ? Some of my pain point are : - How to model a rule in a nosql database ? - How to apply it seamlessly on the Product objects etc.
16 Replies
LazyGuard
LazyGuardOP2y ago
Another precision that I need to make here : Sometimes, I can have rules that are conflictual. For example: * if Category == t-shirts and CustomerId ==2 then set the property LocationInWarehouse to D * if Category == t-shirts and then set the property LocationInWarehouse to C Thus, each rule must have a Priority to resolve such kind of conflicts. The Rule with highest priority is applied.
ZacharyPatten
ZacharyPatten2y ago
The main problem I see with how you have phrased your scenario is this... If the categorization/validation "rules" of a product are dynamic, then what happens when the categorization/validation rules are changed after there are already products in the system and they are considered invalid after the rule change? --- In other words... you sound like you should have normal fields and properties, and then you should have other properties that are determined. For example, don't store "Dangerous" as a field in the database/storage. just have a method to determine if a product is "Dangerous" and call the method each time you need to check "normal properties" versus "computed property"
LazyGuard
LazyGuardOP2y ago
No actually, there is two options: The user is asked whether he likes to apply the new rule on existing products in the database or not. and most likely we need to apply them on existing products to change the value of Dangerous based on the new rule
ZacharyPatten
ZacharyPatten2y ago
well then you sound like you have your answers: - apply the rule when a new product is added - when a rule is changed ask the user if they want to apply to existing products as for modeling the rules in the database, they need to be composed of the primitive data types just like any other model. It will be tricky as you are figuring out, because figuring out how to model logic is hard. right now you may only need threshold logic (if xx is greater than yy) and thus you may only add that kind of model, but in the future you may need more complex logic like (if xx < yy and zz != aa) modeling logic in a database is hard
LazyGuard
LazyGuardOP2y ago
yeah, that's the issue I am trying to wrap my head around. As you said conditions that be somehow complex
ZacharyPatten
ZacharyPatten2y ago
I used to work on a tax processing software and we had a bunch of rules that were modeled in a database and we ran the rules on all a company's financials to determine how much tax they owed the rules were dynamic, but it was a very complex system. not trivial
LazyGuard
LazyGuardOP2y ago
yeah, that kind of complexity scares me. If it was my project I would not do such dynamic rules but the product owner is not ok :/ I think, on the long run, this may result in a completely unusable system due to high complexity
ZacharyPatten
ZacharyPatten2y ago
very possibly. just try to design it to be as flexible as possible. it will be impossible for you to predict the future. you have no idea what kind of rules will be requested maybe define some fundamental rules such as "ThreshholdRules" and have a different table for each type of rule. "ThresholdRules" might have an - id - name - description - property of a product that is being thresholded - value of the product's property that is being thresholded - whether the threshold is an upper or lower bound and then you could have other rule types like "EqualsRule" that just means a property must be equal to a given value. - id - name - description - property of a product - value of the product's property might be terrible advice... but just trying to help but if each type of rule is in it's own table... theoretically you could add rule types in future by adding new tables
LazyGuard
LazyGuardOP2y ago
yes, that's the strong point of such a design I think, adding a new rule would not result in changing the model of existing rules of other type... in nosql terms that would result in different collections but the problem is that there is a risk of having too many tables/collections at the end x<y and z != w is a kind of rule and x ==y and z ==w is another kind
ZacharyPatten
ZacharyPatten2y ago
yeah... that is why in general people try to keep "logic" out of the database ignoring stored procedures and such anyways... good luck 🙂
LazyGuard
LazyGuardOP2y ago
thanks a lot 🙂 I have a terrible idea but maybe it is somehow intresting : Store each rule as following { dangerous : true conditionsOfApplications : "x < y and z != w and j == k" } So the conditions are just stored as a string. Then in the program we parse that string and transform it into logic. Something similar to how a compiler works
Pobiega
Pobiega2y ago
I can only agree with ZP, keep this logic in some kind of "EntityRuleEnforcer" that is run upon all objects either after creation or modification.
LazyGuard
LazyGuardOP2y ago
yeah but how to store things like "x < y and z != w and j == k" that are dynamically set by the user without having to modify the code after every new rule
Pobiega
Pobiega2y ago
My initial thought was to have each "rule" be a Func<Entity, bool> and Action<Entity> if the func returns true for a given object, the action is applied The problem there being that its hard to have "user created" funcs
LazyGuard
LazyGuardOP2y ago
yup, that "user-created" aspect is what makes it very hard
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.

Did you find this page helpful?