We are developing an application that will be a blend of the MVC design pattern and MS' Automation model (used in Office apps). The challenge I have yet to overcome is figuring out to work the business rules that govern when the various "chrome" features are visible, enabled, etc.
For instance, we have a "File" menu with a "New" item that expands to show a "Project" sub-item. To create the menu, we have the following code in our Application class:
void Application()
{
_menus = new MenuList(this);
MenuItem fileMenu = new MenuItem("File");
_menus.Add(fileMenu);
MenuItem newMenu = new MenuItem("New");
fileMenu.Items.Add(newMenu);
MenuItem projectItem = new MenuItem("Project");
newMenu.Items.Add(projectItem);
}
This is pretty straightforward. We would setup our default menus and items here with the collection exposes as the Menus property in the Application object so that other items can be added, removed, etc. by other code. Where I have hit a roadblock is in implementing the necessary logic to handle the Visible and/or Enabled properties for the individual items.
In the example, the "Project" item's Visible property is based on the current user's security. So, something like:
projectItem.Visible = Projects.CanAddObject();
At first, it seems like simply adding this statement to the Application object's constructor is a good approach. But, we are dealing with a web-app, for one, and we have workflow affecting the object's state which may, in turn, change the value of the CanAddObject() call - okay, maybe not this one, but it would affect what the user is allowed to do. For instance, a user may be allowed to perform a certain task only when the object is in a certain state - such as an Approve menu item which would only be applicable if the user had the required security to grant approvals AND the object was in the ApprovalPending state. Furthermore, because we are using MVC with the Automation model, our UI components aren't responsible for any of this logic, only rendering based on what they get from the Application.Menus property.
We are looking at storing the Application object in Session so that it doesn't have to be completely reconstructed with each post-back. However, by doing that, we need some other method to apply the rules that affect the visibility and enabling of each menu item becuase we can't assume that the visibility and/or enabling of the item stays the same since it was first instantiated.
Our logic (item behavior) can be broken down to:
So, we can embed this logic into our MenuItem object such as:
public Boolean Enabled
{
get
{
return ValidationRules.IsValid;
}
}
We could do the same with the Visible property by using the AuthorizationRules collection (which in our case is slightly modified from the base CSLA because we use Rights-based security so we would be checking for the individual right, CanAddProjects, rather than a specific role - cuz more than one role could have this right). Nonetheless, we could have an authorization rule that checks to make sure the current user has the specified right necessary to display the item.
What I see as being the benefit of this approach is that the rules are checked every time the Enabled or Visible property is read. So when our UI component rebuilds itself and checks these properties, we get a return value that reflects the state of that menu item at that time. This seems to take care of that problem - even with a web app where we've stored Application in Session - and allows us to stay consistent with MVC and keep the controlling logic out of the UI/View.
But...
Now we get to the question of responsibility. I cannot see creating - I mean hard-coding - a separate business object for each and every menu item, toolbar button, etc. in the application so that we can follow this approach. So, does that mean that it continues to be the Application object's responsibility to set all of this up?
void Application()
{
_menus = new MenuList(this); MenuItem fileMenu = new MenuItem("File");
_menus.Add(fileMenu);
MenuItem newMenu = new MenuItem("New");
fileMenu.Items.Add(newMenu);
MenuItem projectItem = new MenuItem("Project");
newMenu.Items.Add(projectItem);
newItem.AddValidationRule(CommonRules.CollectionNotEmptyRule);
projectItem.AddAuthorizationRule(Rights.CanAddProjects);
}
Obviously this is not consistent with CSLA because the AddValidationRule and AddAuthorizationRule methods are protected scope.
All of that being said, I am looking for a sanity check, alternative approaches, whole new ideas, etc. to accomplish this. We can't be the first to walk this path, so any insight, guidance, suggestions and/or redirection is greatly appreciated.
Thanks in advance.
This sentance:
RockfordLhotka:First, and most importantly, the business logic for validation and authorization rules should be in the model, not the view or presenter/controller. This is why those methods are protected - they should NOT be called by the UI layer, as that would break down the seperation of concerns.
is exactly what has me confused. Are you saying that our MenuItem objects should be BOs as well?
And, I did not think that our security/authorization process was more complicated than the typical CSLA app, but it isn't necessarily the four basic autho methods that I am having trouble with. These will be straightforward and the static methods will work fine. The two areas I am having difficulty with are:
To accomodate the requirement to have rights beyond the basic four (Add, Get, Delete and Edit), we have a SecurityManager class that handles our authorization responsibilities via its Authorize method. It accepts the specific right that we are authorizing and handles the rest. This is slightly different from the core CSLA implementation in that we've externalized the authorization process (at least the part where we check if the current user has the requested right) and check against the users specific rights. So, for instance, our CanAddObject method in our Project class looks like:
public static System.Boolean CanAddObject()
{
return SecurityManager.Authorize(Rights.CanAddProjects);
}
Part of the reason that we've used this approach is because our users have the ability to administer their own security. So they can add/remove/modify users and groups/roles as well as how users are assigned to each. To hard-code what "roles" are authorized to perform a specific task would negate this feature. We use individual, task-based rights in lieu of roles so that our objects can remain hard-coded and security is enforceable in a dynamic setting. This way, when the end-users create a new group/role, they assign the specific rights they want that group/role to have and the application continues working like a charm.
We do continue to use the property-level authorization within our objects as well but the AuthorizationRules object delegates to the same SecurityManager to check for the required rights.
That being said, our overall architecture includes a web control that renders the menus and items based on the structure defined in our object model (for lack of a better way to refer to it) - via the Application.Menus property. Basically, it iterates over the collection(s) and constructs the UI elements based on the structure and properties found in the object model (a sort of manual data-binding). Our object model version of a menu item (MenuItem) simply defines the item's name and whether it is visible and/or enabled. It is up to the UI component, during rendering to implement these properties, then, in the case of our web app, it is up to the Theme, CSS, skin, etc. to handle the actual presentation. We have successfully implemented this approach using Windows.Forms.Menu, WebControls.Menu and Infragistics.UltraWebMenu interfaces thus far based on our object model. I believe this is consistent with MVC. But, we are at a roadblock when it comes to properly implementing this kind of logic into this model.
The issue that has led me to post this topic is back to how we determine the Visible and Enabled properties for each MenuItem (object model, not UI element). Who's responsibility is it to set these properties or, in other words, who is it that must possess the knowledge of what criteria are used to determine the value for each property?
In the case of the Approve menu item:
Visible = SecurityManager.Authorize(Rights.CanApproveProjects);
Enabled = (currentInstance.State == ProjectState.ApprovalPending);
You can see that the enabled property is based on the current instance and current state of that instance. So, to further complicate things, we can't just have this statement when the menu item is created unless we plan on recreating it each and every time we need to evaluate these properties. This is why I thought it would be better to encapsulate these conditions within the MenuItem itself so that the conditions were re-evaluated each time the property was called.
If you are suggesting that our object model MenuItem class should be a business object itself, then wouldn't it be consistent and logical for it to have its own validation and authorization rules? And, as such, having these conditions within the Visible and Enabled properties would be a good way to accomplish our goals?
But, that brings us back to the question of settings this up. If we create our MenuItem class as a BO, with AuthorizationRules and ValidationRules collections, then we either need to create a separate class for each and every menu item in our application, such as ApproveProjectMenuItem, so that we can define the rules or we need some way for another object to define them. At this point, we have more than 50 menu items possible in our application with a design that allows us to extend this as the application is extended. Isn't there a better way than creating 50 individual menu item classes?
I hope this explanation makes what we are doing and thinking a bit clearer. Thanks for the feedback and continued assistance.
So, if I'm understanding you correctly, you are saying that we should be putting the logic necessary to set the Visible and Enabled properties based on the object's authorization and validation rules in the menu item because it is the MenuItem's responsibility to determine these values? I agree with that but have struggled with the best way to do it. As you said, we want to do this in the most abstract way as possible to not only minimize the amount of code but to allow for reuse of the code with other applications.
Would this be a good candidate for the use of the new System.Predicate<T> delegate? I first looked into this a while ago but have yet to implement it anywhere. My understanding would be that we could define an external method to use to determine these properties from another class, such as our MainForm or Application class when the MenuItem is instantiated. For instance,
_menuItem = new MenuItem("Approve", new System.Predicate<Project>(CanApproveObject));
where CanApproveObject is a static method in our Project class that accepts an instance of Project and returns a boolean value based on the authorization rules defined for the object. This would allow us to implement business rules, such as checking the current state of the object, and keep all of that within the Project class itself.
Is this a correct and appropriate way to use a Predicate? And do you agree that this is a good approach to use for our situation?
Of course, in order for this to work, our menu item would have to have a reference to the current Project instance to evaluate.
Still not quite there.........
I'm with you now. Just have to think about how to implement. And, I'd still like to find a way to accomplish it without having to create a separate class to represent each menu item, such as ApproveContractMenuItem.
Hafta give it some more thought, but you've definitely helped me with the concepts.
Any other thoughts on how to implement are appreciated.
From: SonOfPirate [mailto:cslanet@lhotka.net]
Sent: Monday, November 13, 2006 10:39 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] MVC Pattern/MS-Automation Model w/ CSLAI'm with you now. Just have to think about how to implement. And, I'd still like to find a way to accomplish it without having to create a separate class to represent each menu item, such as ApproveContractMenuItem.
Hafta give it some more thought, but you've definitely helped me with the concepts.
Any other thoughts on how to implement are appreciated.
It has occurred to me that the answer may have been staring me in the face the whole time! The Document!
What I mean is, the menu items that I am having a problem resolving are using per instance rules and the difficulty has been trying to figure out how to get the menu item to "know" these rules when, as you said, they shouldn't. But nothing else was apparent until it occurred to me that the "instance" in which we are referring is actually in the object model as the "document". And, fact is, the document is what added the menu item to the default menus in the first place! (At least that's how it works with MVC and context menus, right?)
So, all of my default menu items, those created by the main form or Application object are "global" in nature and only use per type rules such as CanAddObject(). These are easily captured and managed by the form or Application classes. If we leave the responsibility of adding and managing context related items to the document to which they are related, the menu item doesn't need to know anything about the object or the rules. Instead, the child form (or whatever) can apply the rules to determine if the item is to be added and, if so, whether it is enabled or not.
I will have a Project class which inherits from Document (and/or implements IDocument) that is rendered using the Project web control (or page) and manages the creation and handling of the Approve menu item whenever the current user has the necessary rights. And, it will mark the menu item as enabled when the Project BO that is the data source for the document is in the Approval Pending state.
This has just occurred to me and I have some more thinking to do to work through it, but I think this may solve the problem and actually be even more in line with MVC, etc. In fact, this may allow me to have the MenuItem be the single class I was looking for. The document controls its creation and adds the hook for the event handler when it is clicked so that it can be handled correctly.
Make sense?
SonOfPirate,
This thread is very interesting. I was wondering if you use any databinding in your winforms for this MVC style? My question is that originally when I tried something very similiar, It seemed that my UI classes had to implement a lot of interfaces for databinding that the internal CSLA object had implemented all ready. There's really nothing wrong with this it was just took a lot of code to hook together. I'm just wondering how you wired your UI objects to the CSLA business objects.
Thanks,
HappyJack
RockfordLhotka:
One of the most interesting comments in David West's 'Object Thinking' book is where he talks about the MVC pattern and points out that "controller" is a terrible word. It implies that the controller is in control, which can not be in a real OO model. Objects don't control other objects. He suggests using the word coordinator, to better convey that the "C" is merely there to coordinate interactions between the View and Model.
Copyright (c) Marimer LLC