Best practice for evaluating business rules 'in context'?

Best practice for evaluating business rules 'in context'?

Old forum URL: forums.lhotka.net/forums/t/4147.aspx


GlennA posted on Thursday, January 10, 2008

Hi all,

Evaluating CSLA for a new project and running into questions regarding running a business rule in the context of the operation being performed.  It appears that in CSLA, the objects have a sense of their own business rules, but not necessarily of rules that should be applied in relation to other objects in the operation.

Making up a example on the fly here...

Let's say I have a form on which I'm entering data for a new Factory, as well as a new Customer.  Each of these objects has a country associated with it.  For whatever reason, we don't want these country values to be the same (probably not a great example, but bear with me).  So if one enters France for the country values of both of these objects, the business rules pass for the objects individually but there's no check done of the other object to see if these country values are equal.

In the past, I might have hooked up the submit to a static class on the server checking this like:

public static SubmitCustomerAndFactory(Customer customer, Factory factory, ...)

{

      if (customer.Country == Factory.Country)

      throw...

}

It's probably not too hard to pick apart this example, but hopefully the point was expressed.  How might one use the business rules framework to apply rules not only to individual objects, but *across* objects depending on the operation?  The mobile business objects are intelligent, but aren't necessarily aware of the overall context they're contained in.  How might this be worked around without coding this logic invidually into client and server? 

Thx,

Glenn

 

 

 

 

 

 

 

ajj3085 replied on Thursday, January 10, 2008

Yes, not a great example.  Smile [:)]

If you're entering both sets of data at the same time though, the use case suggests that one of these would be a root object.  That object would then check its child to see if the country is the same.   If it was, it would report a broken rule (and Save will throw if you attempt to save an invalid object).

Does that help?
Andy

GlennA replied on Thursday, January 10, 2008

That helps to some extent.

What I'm really driving at though is that having business rules only within the individual objects isn't always realistic.  There are often rules that depend on the context of the overall operation in which the object is contained, whether they're rules within the objects in question that might or might not be applicable, or rules relating to different objects in the operation.  In a perfect rule, the classes and their relationships can be configured so that there are parent-child relationships like this, but this doesn't always happen in practice as the use cases get larger.

Maybe I can come up with a better example...  Smile [:)]

JoeFallon1 replied on Thursday, January 10, 2008

Glenn,

My BOs are a bit on the data centric side but I think that is OK because I usually wrap them in a Root level BO which is a Use Case Controller object. I originally called it a Unit of Work but later learned that means something different than what I am using it for - so all my controler objects end with a 'UOW' suffix.

Anyway, the point is that for a given use case, the UOW object is loaded and then fetches the other BOs required to satisfy the use case. They can be root BOs with child collections, or a bunch of NVLs or, whatever you need to fetch.

The UOW object thus knows which other BOs are contained in it and you can write a set of high level rules which cross each of the lower level contained BOs. You also override IsValid and IsDirty so that you know if the UOW is valid or dirty by looking at its contained objects as well as any properties it may have on its own.

This pattern has worked very well for me (for over 4 years) and the Codesmith templates that I use to generate BOs.

Joe

 

 

GlennA replied on Friday, January 11, 2008

That sounds like one of the approaches I was considering.  The back-end may have to support several versions of the front-end, so I'm intentionally keeping the BOs more granular than if I knew all of the specifics of the front end.  The business cases will change to some extent as well, so granularity might better support this.  The 'wrapper' class you say you're using sounds like it might fit the bill.

Mind sharing more of the specifics of an implementation of this wrapper (maybe just pseudocode if it's lengthy)?  Anything out there you're recommend for starting code generation for my table-centric cases?  Or did you roll your own from scratch?

Thanks again to all repliers,

Glenn

ajj3085 replied on Friday, January 11, 2008

Well, I'm not discussing a wrapper class as much as a business object.  The business object exists to fulfill a use case, so it doesn't matter at all where the data comes from or how the data is stored.  The bo "knows" where to go to get and put data.

If your business cases change slightly, you'll likely need a whole new set of objects to fulfill them.  Possibly in another assembly.  Keep the types which all application use in a shared assembly and the ones specific to each application in their own.  Only if the behaviors (i.e., your business rules) are the same can you look at consolidating the object sets together.

You can do code generation, and my people here do.  The drawback in my mind is that all the generation tools I'm aware of generate classes based on database tables.  If you do that, you're already starting down the wrong track.  Csla can handle that, but it's philosophy is to support behavior based design, not data based.  So you don't want to subclass based on the data the class will contain, but based on the rules it enforces.

If you want samples of business objects, check out the PTracker sample that comes with Csla.  Also, if you haven't already, you should probably get the book.  This will explain how the framework is built and how to build a business library from use cases.  That last part is where most people struggle... we tend to be taught data driven design.

ajj3085 replied on Friday, January 11, 2008

Well remember, you're buildings BOs to satisfy use cases.  Those BOs normally AREN'T reused anywhere else.  So if you need some context to perform a rule check, you need to have that as part of one of your BOs state. 

BOs can get data from anywhere, they don't (and probably shouldn't) map to your database tables.  So when designing your BO, if it needs some context data, it needs to get that data as part of its instantiation.

Sometimes, you can't be 100% sure if the rule is violated until you actually hit the DB as well.  Csla can handle this scenario too; you run your rule check in the DataPortal and if its violated, thrown an exception.  Not the best as UX goes, but there's not much else you can do to overcome it.  You can use a command object to check before the user saves,but you will always need to check again during the save.

Does that clear things up a bit?

Andy

Copyright (c) Marimer LLC