Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Business Rules #30

Open
1 of 4 tasks
charlessolar opened this issue Jun 7, 2018 · 2 comments
Open
1 of 4 tasks

Business Rules #30

charlessolar opened this issue Jun 7, 2018 · 2 comments

Comments

@charlessolar
Copy link
Owner

charlessolar commented Jun 7, 2018

Running rules in entities currently involve a lot of If's thens, and throws

We can make this easier.

There's a few changes I can make right off the bat - adding a Rule method on entities to validate against the state of the entity automatically.

   public void PostInvoice() {

       // Instead of....
       if( State.Address == null )
           throw new BusinessException("No address set");
     
       // Do this!
       Rule("Address Set", x => x.Address == null, "No address set");

       Apply<Events.Posted>();            
   }

In the long term however we want to support dynamic rules and validation against data that may not be inside the entity. If for instance we want to make sure the customer is in good standing before posting invoice - we'd have to load the customer entity to check. And a business manager might want to override a rule or create his own rules without involving a programmer to do so.

The above works for static validation - so lets also add dynamic!

I want each public method on a entity to be available for rule creation dynamically via the app.

I envision some kind of extendable entity validator. Something like

    RuleFor<Invoice>()
        .When(x => x.PostInvoice)
                .Rule("Address Set", x => x.Address == null, "No address set");
    

we already know how to serialize expressions so aggregates.net would just need to keep track of rules for entities and apply them while executing commands and events.

public Task Handle(Commands.AddRuleToInvoices command, IMessageHandlerContext ctx) {
    Aggregates.For<Invoice>()
              .When(command.Action)
              .Rule(command.Name, command.Expression, command.Message);
}

Dynamically loading other entities for checking

Aggregates.For<Invoice>()
    .When(x => x.PostInvoice)
    .Load<Customer>(x => x.CustomerId)
    .Load<PaymentMethod, Customer>(customer => customer.DefaultPaymentMethodId)
    .Rule<PaymentMethod>("Payment Is CreditCard", method => method.Type == Methods.CreditCard);

Todos

  • Static RuleFor on entities
  • Expressive rules on entity types
  • Dynamic rules via serialized expressions
  • Test generation for seeing new rules in action (?)
@charlessolar
Copy link
Owner Author

Defined rules would be internal aggregate.net data - which would mean an internal aggregates.net stream. Perhaps the projection we're planning in #33 can be useful

@charlessolar
Copy link
Owner Author

Should accomplish this via a pipeline behavior

Rules can be defined on commands, the behavior would load and evaluate any dynamic rules on a command

A user would add/edit/remove rules via other commands

public Task Handle(Commands.AddRuleToInvoices command, IMessageHandlerContext ctx) {
    Aggregates.Command<PostInvoice>()
        .Load<Invoice>(x => x.InvoiceId) // on PostInvoice command
        .Load<Invoice, Customer>(x => x.CustomerId) // on Invoice state object
        .Load<Customer, PaymentMethod>(x => x.DefaultPaymentMethod) // on Customer state object
        .Rule<PaymentMethod>("Payment is CreditCard, x => x.Type == Methods.CreditCard);

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant