3

If I have a Policy and this Policy should consist of Sections(fixed number).

My Sections are 4 predefined sections:

  • Work time regulations.
  • Excuses.
  • Shifts.
  • Time-sheets.

Each Section has fixed attributes differ from the attributes in other sections . If I can illustrate it as an analogy:

  • Policy ---> Human body.
  • Sections --->(Arms, Legs, Head)
  • Each section differs from one another Like (head contains eyes, ears,...etc rather than the arms contains two hands)

For example:

  • Work time regulations section has name,list of work-times.
  • Excuses section has numberofhours, reason, excuse_type.

Note: based on the domain expert explanation: He want a Save action with each section (as a draft) so that he can update the section unless the Policy not submitted yet, and a Submit action for the whole Policy so that after the Policy submission No one can update or delete this Policy or its sections. (any required update = Define new Policy)


Now I want to design the Policy, Section and its content. but I'm Stuck.

Firstly I thought I could design Policy as Entity (the aggregate root) and create four classes, one for each Section and inherit them all from Section base class(Id,name), and The Policy contains List of Section.


Secondly I direct my thinking to generalize the section content in the following way:

I will create:

  • Interface ISection : SectionType,SectionRule
  • Every Section will implement this Interface

Then I will create Reference Table SectionRules:

EX:

rule-key         |default-value|operators|section-type|value-type|rule-type

NumOfhoursInMonth| 5:00        |  =      |  2         | String      |Assignment 
AvailableExcuses |2:00,2:30    |  IN     |  2         | List<String>|Relational 

Notes :

  • section-type 1 is Excuses
  • Operators are Enum
  • Section-types are Enum

When The user initiate Policy I will loop through the ref table to list the Rules in a form so that he could change the default values and save them in Section based on its type like this:

  Id   |Name       |Rule                   |section-type
  1    |Excuses    |NumOfhoursInMonth <= 6 |   2

I face two problems right now.

  1. How to correlate different rules if some of them dependent on each other? Ex NumOfExcuses'hoursInMonth Should be less than or equal 6:00 according to the first rule, but how to prevent the user from violating this rule during setting the second rule If he set the AvailableExcuses IN(5:00,7:00)! Now I should prevent the user from add a number greater than 6 because the first rule restricts the second one ? The second rule is inconsistent with the first rule because the list contains (07:00) and the first rule states that totalExcuseshoursInMonth <= 06:00 hours
  2. How to make the rules more expressive to allow conditional rules and other rules?

Am I in the right direction? Could I get some recommendations in my case?

plalx
  • 42,889
  • 6
  • 74
  • 90
Anyname Donotcare
  • 11,113
  • 66
  • 219
  • 392
  • I think your question is pretty opinion-based. 10 developers will propose 10 approaches, and probably all of them will have different advantages and disadvantages. Can you maybe explain where exactly you are stuck? What is wrong with the design you proposed yourself? – dymanoid Jan 16 '19 at 15:35
  • 1
    What issues you dont know how to solve? What's the problem with the large `Policy` AR? – plalx Jan 16 '19 at 16:07
  • @plalx I edit my question, Could u take a look please – Anyname Donotcare Jan 16 '19 at 17:41
  • 2
    This is not the type of question that can be asked at Stack Overflow. You're asking us to propose a complete design (*Now I want to design the Policy, Section and its content. but I'm Stuck.*) from a very brief set of requirements. You may be able to salvage the question (and the bonus) if you can confine it to an actual programming question that's just a part this task. – Gert Arnold Jan 17 '19 at 08:16
  • @GertArnold: This's a design question not a very technical one, I explain how I thought to solve my problem, and my two questions are about my design, Is in the correct direction ? and how to make a correlation between different rules, and making these rules more expressive( this's the only technical issue) – Anyname Donotcare Jan 17 '19 at 08:21
  • That's the thing. Questions at Stack Overflow are supposed to be technical, preferably *very technical*. You're disqualifying your own question now as on-topic (i.e. broad/opinion based). Focus on the only technical issue. Or try another platform, f.e. [Software Engineering](https://softwareengineering.stackexchange.com/help/on-topic). – Gert Arnold Jan 17 '19 at 08:30
  • @AnynameDonotcare I'm really trying to have a clear understanding of the problem domain and what you are struggling with. Are you basically trying to build a rule engine that can determine the valid state of an entity (e.g. Policy), where rules may be bound to specific fields, may be dependent of multiple fields and where you want to be able to detect when rules are conflicting with each others (e.g. `Field1: Len(>10)` and `Field1: Len(<10)` are conflicting)? – plalx Jan 20 '19 at 02:14
  • @plalx: `Are you basically trying to build a rule engine` yeah this's a part of my domain and my most concerns are `1-validate the correlation between different rules in every section of the policy` and `2-Compose the rules in most expressive way to express the rules in semantic way` – Anyname Donotcare Jan 20 '19 at 08:23
  • 1
    @AnynameDonotcare I have posted an answer and tried to give you some ideas on how to approach the problem. – plalx Jan 20 '19 at 20:07
  • 1
    FYI - I have reworded your question to fit the problem a little better and I updated tags as well. Feel free to revert the changes. – plalx Jan 22 '19 at 15:03
  • @plalx : Thanks a lot for every thing – Anyname Donotcare Jan 22 '19 at 18:59

2 Answers2

4

I'm not entirely sure what design would be most suitable and you will certainly have to go through multiple model iterations until you are satisfied, but I think the core of the problem, which I assume is composing rules and finding conflicting rules could be solved using the Specification Pattern.The Specification Pattern basically consists of making the rules first-class citizens of the model, rather than having them only expressed through conditional language constructs.

There are many ways to implement the pattern, but here's an example:

Specification Pattern

In one of the systems I have designed1, I have manage to reuse set same set of specifications to enforce authorization rules of commands & queries and enforce & describe business rules.

For instance, you could add a describe(): string method on your specifications that is responsible to describe it's constraints or a toSql(string mainPolicyTableAlias) method which can translate it to SQL.

e.g. (pseudo-code)

someSpec = new SomeRule(...).and(new SomeOtherRule(...));
unsatisfiedSpec = someSpec.remainderUnsatisfiedBy(someCandidate);
errorMessage = unsatisfiedSpec.describe();

However, implementing such operations directly on the specifications might pollute them with various application/infrastructure concerns. In order to avoid such pollution you may use the Visitor Pattern, which would allow you to model the various operations in the right layer. The drawback of this approach though is that you will have to change all visitors every time a new type of concrete specification is added.

Visitor pattern

#1 In order to do so I had to implement other specification operations described in the above-mentioned paper, such as remainderUnsatisfiedBy, etc.

It's been a while since I've programmed in C#, but I think that expression trees in C# could come very handy to implement specifications and transform them into multiple representations.

validate the correlation between different rules in every section of the policy

I'm not entirely sure what you had in mind here, but by adding an operation such as conflictsWith(Spec other): bool on your specifications you could implement a conflict detection algorithm that would tell you if one or more rules are in conflict.

For instance in the sample below both rules would be conflicting because it's impossible for both of them to ever be true (pseudo-code):

rule1 = new AttributeEquals('someAttribute', 'some value');
rule2 = new AttributeEquals('someAttribute', 'some other value');
rule1.conflictsWith(rule2); //true

In conclusion, your entire model will certainly be more complex than this and you will have to find the right way of describing the rules and associating them with the right components. You may even want to link some rules with applicability specifications so that they only apply if some specific conditions are met and you may have many various specification candidate types, such as Policy, Section or SectionAttribute given that some rules may need to apply to the whole Policy while other kind of rules must be interpreted given a specific section's attribute.

Hopefully, my answer will have sparked some ideas to put you on the right track. I would also recommend you to have a look at existing validation frameworks & rule engines for more ideas. Please also note that if you want the entire rules and the state of the Policy to be consistent at all times then you will most likely to design the Policy as a large aggregate formed of all sections & rules. If somehow that is not possible nor desirable because of performance reasons or concurrency conflicts (e.g. many users editing different section of same policy) then perhaps you will be forced to break down your large aggregate and use eventual consistency instead.

You will also certainly have to consider what needs to be done when existing state is invalidated by new rules. Perhaps you will want to force the rules & state to be changed at the same time or you may implement state validation indicators to mark parts of the current state as invalid, etc.

1-Could You explain more about describe(),toSql(string mainPolicyTableAlias),I didn't understand the intent behind these functions.

Well, describe would give a description of the rule. If you need i18n support or more control over the messages you may want to use a visitor instead and perhaps you'd also want a feature where you may override the automated description with templated messages, etc. The toSql method would be the same, but generate what could be used inside a WHERE condition for instance.

new Required().describe() //required
new NumericRange(']0-9]').if(NotNullOrEmpty()).describe() //when provided, must be in ]0-9] range

This's a considerable drawback ! Could I ask how to overcome this problem.

Supporting behaviors directly on objects makes it easy to add new objects, but harder to add new behaviors while using the visitor pattern makes it easy to add new behaviors, but harder to add new types. That's the well-known Expression Problem.

The problem can be alleviated if you can find a common abstract representation that is unlikely to change for all your specific types. For instance, if you want to draw many types of Polygon, such as Triangle, Square, etc. you could ultimately represent all of them as a series of ordered points. A specification can certainly be broken down as an Expression (explored here), but that's not going to magically solve all translation issues.

Here's a sample implementation in JavaScript & HTML. Please note that the implementation of some specifications is very naive and will not play well with undefined/blank/null values, but you should get the idea.

class AttrRule {
  isSatisfiedBy(value) { return true; }
  and(otherRule) { return new AndAttrRule(this, otherRule); }
  or(otherRule) { return new OrAttrRule(this, otherRule); }
  not() { return new NotAttrRule(this); }
  describe() { return ''; }
}

class BinaryCompositeAttrRule extends AttrRule {
  constructor(leftRule, rightRule) {
    super();
    this.leftRule = leftRule;
    this.rightRule = rightRule;
  }
  
  isSatisfiedBy(value) {
    const leftSatisfied = this.leftRule.isSatisfiedBy(value);
    const rightSatisfied = this.rightRule.isSatisfiedBy(value);
    return this._combineSatisfactions(leftSatisfied, rightSatisfied);
  }
  
  describe() {
    const leftDesc = this.leftRule.describe();
    const rightDesc = this.rightRule.describe();
    return `(${leftDesc}) ${this._descCombinationOperator()} (${rightDesc})`;
  }
}

class AndAttrRule extends BinaryCompositeAttrRule {
  _combineSatisfactions(leftSatisfied, rightSatisfied) { return !!(leftSatisfied && rightSatisfied); }
  _descCombinationOperator() { return 'and'; }
}

class OrAttrRule extends BinaryCompositeAttrRule {
  _combineSatisfactions(leftSatisfied, rightSatisfied) { return !!(leftSatisfied || rightSatisfied); }
  _descCombinationOperator() { return 'or'; }
}

class NotAttrRule extends AttrRule {
  constructor(innerRule) {
    super();
    this.innerRule = innerRule;
  }
  isSatisfiedBy(value) {
    return !this.innerRule;
  }
  describe() { return 'not (${this.innerRule.describe()})'}
}

class ValueInAttrRule extends AttrRule {
  constructor(values) {
    super();
    this.values = values;
  }
  
  isSatisfiedBy(value) {
    return ~this.values.indexOf(value);
  }
  
  describe() { return `must be in ${JSON.stringify(this.values)}`; }
}

class CompareAttrRule extends AttrRule {
  constructor(operator, value) {
    super();
    this.value = value;
    this.operator = operator;
  }
  
  isSatisfiedBy(value) {
    //Unsafe implementation
    return eval(`value ${this.operator} this.value`);
  }
  
  describe() { return `must be ${this.operator} ${this.value}`; }
}

const rules = {
  numOfHoursInMonth: new CompareAttrRule('<=', 6),
  excuseType: new ValueInAttrRule(['some_excuse_type', 'some_other_excuse_type']),
  otherForFun: new CompareAttrRule('>=', 0).and(new CompareAttrRule('<=', 5))
};

displayRules();
initFormValidation();

function displayRules() {
  const frag = document.createDocumentFragment();
  Object.keys(rules).forEach(k => {
    const ruleEl = frag.appendChild(document.createElement('li'));
    ruleEl.innerHTML = `${k}: ${rules[k].describe()}`;
  });
  document.getElementById('rules').appendChild(frag);
}

function initFormValidation() {
  const form = document.querySelector('form');
  form.addEventListener('submit', e => {
    e.preventDefault();
  });
  form.addEventListener('input', e => {
    validateInput(e.target);
  });
  Array.from(form.querySelectorAll('input')).forEach(validateInput);
}

function validateInput(input) {
    const rule = rules[input.name];
    const satisfied = rule.isSatisfiedBy(input.value);
    const errorMsg = satisfied? '' : rule.describe();
    input.setCustomValidity(errorMsg);
}
form > label {
  display: block;
  margin-bottom: 5px;
}

input:invalid {
  color: red;
}
<h3>Rules:</h3>
<ul id="rules"></ul>

<form>
  <label>numOfHoursInMonth: <input name="numOfHoursInMonth" type="number" value="0"></label>
  <label>excuseType: <input name="excuseType" type="text" value="some_excuse_type"></label>
  <label>otherForFun: <input name="otherForFun" type="number" value="-1"></label>
</form>
plalx
  • 42,889
  • 6
  • 74
  • 90
  • WooooW, This's a great explanation, Thanks a lot, I'm really grateful. I have some questions please. 1-Could You explain more about `describe(),toSql(string mainPolicyTableAlias)`,I didn't understand the intent behind these functions. 2- `The drawback of this approach though is that you will have to change all visitors every time a new type of concrete specification is added.` This's a considerable drawback ! Could I ask how to overcome this problem. – Anyname Donotcare Jan 21 '19 at 10:13
  • 3- In first place I don't want to model a way to handle the insertion of `SectionAttributes` I will store them directly in DB without app. and all the effort will be in -How to format these attribute (compose the rules) in semantic an d most expressive way to use them easily in the application. - Display these attributes in sections and can be validated and saved with their inserted values . -Correlate the dependent attributes in the same section when the user try to set their values. – Anyname Donotcare Jan 21 '19 at 10:15
  • @AnynameDonotcare I'm not sure to understand #3 very well. What do you mean by "Correlate the dependent attributes in the same section when the user try to set their values"? Could you give a use case example. Also, I will be able to provide guidelines like I did, but I certainly cannot design your entire system :P – plalx Jan 21 '19 at 15:18
  • I meant that Firstly I will insert all rules with their default values in DB table, and I will not model this part right now at all to not complicate my business. But1- I want the rules with their default values expressed in semantic and expressive manner, So I can display it and allow change its values in every section easily for the user . 2- Keep the correlation between different rules to validate them when the user save every section, where no conflicts between the rules values. – Anyname Donotcare Jan 21 '19 at 18:52
  • "but I certainly cannot design your entire system :P " hhhhh, I'm so grateful for your help, and You are right, I'm Just new in `DDD`, I face many cases, and I'm afraid to start, the fear of failure or that I thought wrong . So I try to learn from U as I can. Sorry If asked so many questions. – Anyname Donotcare Jan 21 '19 at 18:56
  • "I want the rules with their default values". I wouldn't have default values on rules. I would rather have default rules. For instance, policies or sections of a specific type could come with default associated rules (e.g. `AvailableExcusesIn([2:00,2:30])`. That relieves you from modeling the concept of default values and gives you much more flexibility. Depending what types of candidates you rules will deal with (checking attributes vs whole sections) you may write them as generic as you need (e.g. `ComparisonRule('attributeName', operator, value)` or specific as in `AvailableExcusesIn`. – plalx Jan 21 '19 at 19:27
  • @AnynameDonotcare I updated the answer with a sample implementation. – plalx Jan 21 '19 at 21:14
  • U are just amazing, I'm so grateful for your generosity all the time, I learnt from your ideas a lot. really thank u so much. – Anyname Donotcare Jan 22 '19 at 18:57
  • @AnynameDonotcare I'm glad I could help. Let me know if you have other questions and good luck! – plalx Jan 22 '19 at 20:36
1

It seems that you want an object model, presumably for a custom CMS, that will be used to render forms:

  • The Policy is the Form
    • When Submitted, Form is locked
  • Sections are the Fieldsets
    • May be saved independently
  • Section Attributes are the Fields
    • Fields may be populated with initial values
    • Fields are subject to Validation Rules defined elsewhere / dynamically

er diagram

enter image description here

Some things to note:

  • Default values should be captured on SectionAttributes
  • SectionAttributes have ValidationRules

From your question it sounds like there are at least two roles:

  • those who can lock a policy, admins
  • those who cannot lock policies, users

Design Considerations

  • Can Sections be recursive?
  • Who are the actors, admins, users, etc., that are interacting with the system?
  • What are the public operations on each entity?
  • Can SectionAttributeValidationRules be updated after a Policy is locked? What happens when new / updated rules invalidate existing SectionAttributes?
  • Can Sections be reused across Policies?
  • Are Policies access-controlled?

My Advice

  • adhere to good software principles
    • open–closed principle
    • SOLID, DRY, Law of Demeter, and the rest
  • don't worry about making mistakes
  • refactor to patterns
  • leverage test driven design (red, green, refactor)

This is a good start, and really trying to get this 100% upfront is a waste of time; hopefully this helps you get unstuck.

Rafael
  • 7,605
  • 13
  • 31
  • 46
  • Thanks a lot, This's a great explanation, Could I ask : 1- What should ISectionAttribute &IPolicy contain ? 2- Why we use `ISectionAttribute` as a type in Section instead of the concrete class? 3-I don't know how to make the type of Value in `SectionAttribute` as `T` ? 4-Could I ask what do you mean by `Sections be recursive` in this context? 5-Could I ask what do you mean by `access-controlled` in this context? 6- I will be grateful if you recommend me a suitable way to build (Compose)my `SectionAttributes` as rules and value I mean a way to build a complete `Attribue` please? – Anyname Donotcare Jan 20 '19 at 12:28
  • You're welcome! The first two answers depend on what those abstractions need to do. I'm not entirely sure what a IPolicy should have in it, what operations can be made on a Policy? The reason I left those details out is because we'll need to know more about their functions and responsibilities. The reason we use interfaces instead of concrete class dependencies is answered by [the Dependency Inversion Principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle). The basic idea is not to couple classes to each other, but instead to depend on their interfaces. – Rafael Jan 21 '19 at 00:16
  • Think of an automobile: You can sit in *any* car and drive it, why? -- because the *interface*, driver seat, steering wheel, gear changer, etc., is similar *despite implementation details*, 4, 8, or 12 cylinders, different volumes, forced induction, and other details. The reason these components can be swapped out is because their interfaces are consistent: an engine outputs its energy via the flywheel and the transmission has an input and output shaft, but *the two systems are completely black boxes to one another.* Interfaces are extremely powerful, required, for building modular systems. – Rafael Jan 21 '19 at 00:29
  • Can Sections have nested Sections? This is an example of a recursive relationship and it is common in hierarchical structures. About access control, I got the feeling that admins can do certain operations on Policy objects that users cannot, is this correct? – Rafael Jan 21 '19 at 00:35
  • `Can Sections have nested Sections?` No We don't want to make it that complex. No nesting. `I got the feeling that admins can do certain operations on Policy objects that users cannot, is this correct?` I don't want to model a way to handle the insertion of SectionAttributes I will store them directly in DB without app. and all the effort will be in -How to format these attribute (compose the rules) in semantic and most expressive way to use them easily in the application. - Display these attributes in sections and can be validated and saved with their inserted values . – Anyname Donotcare Jan 21 '19 at 10:21
  • -Correlate the dependent attributes in the same section when the user try to set their values. So right now I have one user the admin who can manage all this. – Anyname Donotcare Jan 21 '19 at 10:22