Login Skip Navigation LinksWilsonWebPortal > Forums Search
Download WebPortal Download WebPortal
Download and start using the full WebPortal completely free, including the source for the basic modules so you can see how to create your own if the included modules aren't enough.

PayPal Subscribe
Get It All for $50 USD:
WebPortal, ORMapper,
Source Code, All Updates
PayPal

User Login User Login
Log In
 
 
Reset Password

Wilson WebPortal Forums Wilson WebPortal Forums : Advanced Topics : About the responsibilities of the classes in Wilson.WebPortal.Core

Date Post
4/9/2006 4:27:16 PM

As I have worked through your code, I have noticed that the classes in Wilson.WebProject.Core take on many responsibilities. The User class, for example, has three creation methods: GetById, GetByName, and Create.  The same class has two methods relating to persistence: Create and Save. The same class is involved with authorization (IsInRole), with password management (ResetPassword and ChangePassword), with login management (LoginAttempt and LoginFailed), and with so much more.  Some are static methods, others are instance methods.  The User class, like all of the classes of Wilson.WebProject.Core, is taking on many responsibilities.

Having come into this industry from outside, I have had it hammered into me that each class should have a single responsibility.  I'm sure Scott Ambler and kin would require a UserManager to handle the creation responsibilities, a UserMapper to handle the persistence responsibilities, perhaps an AuthenticationManager to handle the login attempts, and so on, leaving the User class with the single responsibility of representing a user.

Obviously you know all of this, yet you have chosen to ignore conventional wisdom.  The result is a very neat project structure, with a small number of classes, where each class takes on many different, though cohesive, responsibilities.  I very much like the approach, but I'm curious to know whether you take this approach because you think the convention wisdom is flawed, or because you know that the extra complexity of splitting classes is rarely justified, or for some other reason?

4/9/2006 5:20:23 PM Ah, this is a tough one.  First, lets be clear that I think nearly every experienced architect would agree that there are always tradeoffs to be made, even with best practices, and that there are seldom any definitive perfect answers.  That is of course why we talk about architecture, design patterns, and best practices -- they are guidelines that must be merged with real experience and project specifics.

Next, let me say that the User class is one of the few Core classes that have this degree of complexity in my opinion, so I don't completely agree that this one is like all of the others.  But you are correct that there are at least a few "extra" responsibilities that most (if not all) of the Core objects do share -- methods like Get, Create, Save, and Delete for instance.

Now let's digress for a moment and consider these methods -- notice that all (or at least the vast majority) of the Create methods are internal, and Get, Save, and Delete are usually trivial implemenations that have to do with the Cache.  The internal Create methods exist purely so that the initial data can get populated -- I decided against sql scripts since with the ORMapper they would have been far more difficult to maintain and they would not have worked with multiple databases.  I considered some other approaches to "better" separate out this responsibility, and in fact I do have Setup and Install classes to encapsulate everything except for these internal Create methods, but in the end I decided that the additional complexity that was going to be necessary was  not worth it -- a couple of internal Create methods was thus a tradeoff made for greater simplicity and maintainability.

On the other hand, the Get, Save, and Delete methods in my case would almost not be necessary with my ORMapper, except for the fact that we have to worry about the Cache class also -- so I did need the methods somewhere, but they are very simple with little implementation.  Now I agree that a Manager class for each entity object is a "best practice" in some sense, but you also have to keep in mind that the idea behind that "best practice" is to separate out the complex persistence logic which I have already done with my ORMapper and Cache.  So while having a Manager class for each entity can still be argued to be a good thing, its also quite clearly going to violate another "best practice" which is to not unnecessarily create lots of classes -- and in this case I decided that for me the better tradeoff for simplicity and maintainability was to go for fewer classes since these methods we're talking about just don't have much to them in reality.

Is this a "perfect" solution?  No it is not, for there is no such thing, and as always I'm sure that a room full of architects would disagree on a lot of my decisions.  But that's also why you don't ever want a room full of architects -- because there is hardly ever one single best answer!  :)  In the end, it is the job of the architect to weigh the various options and determine what he feels to be the best approach for the specific project at hand and given the various tradeoffs.  But also note that there is always going to be a factor of personal experience and style that cannot, and should not, be ignored -- in my particular case, I prefer fewer classes (where it makes sense of course) more than some others do.

Now to look at some of your other concerns in regard to the User class specifically.  I also struggled with the fact that the User class ended up becoming somewhat more than I would have preferred, but to understand the rationale for my decision to allow this you must look at the Membership class in the Framework project, which is an implementation of the ASP.NET v2.0 MembershipProvider.  When you consider the Membership class I think you will see why most of these other methods were needed somewhere, and while they maybe didn't need to all be in the User class, to me there was an undeniable fact that all of these methods exist for one reason in the overall view -- and thus my decision to again favor fewer classes.  Again its certainly a debateable decision, afterall from a purist point of view my Core User object should not even be aware of the existence of the Framework Membership class -- but I'm also very realistic and I'm not going to deny the truth that I am building a web application based on ASP.NET v2.0.

So I hope you now see a little better what my thought process was in coming to these design decisions.  Do I think conventional wisdom is flawed?  Not at all in this case -- I simply think that such a question is based on reading a few books on architecture and design without considering the greater whole body of experiential knowledge.  In other words, there are often conflicting "best practices", and an architect cannot simply let one very good principle override other principles that are also valid -- thus its often about tradeoffs and only experience can make those decisions, and even then there is never going to be just one single right answer.

Thanks, Paul Wilson
4/9/2006 7:14:47 PM

I've mentioned elsewhere that I'm rebuilding the WilsonWebPortal from the ground up, line by line, class by class.  I am doing this because I want to learn not only *how* you have done things, but *why* you have done things a certain way.  The how I could learn from any book, but the why is something that books rarely talk about.

Thank you for explaining the reasons why you chose fewer larger classes rather than many smaller classes.  I think that the speed, flexibility, and simplicity of the WebPortal vindicates your choices, but I was still curious to know your thought process.

So thank you for your sharing your thoughts.