In my previous post, I analyzed a common mistake made by developers in designing flexible domain models for users: using hierarchical (is-a) relationships to model what are actually aggregate (has-a) relationships.
In this post, I stop analyzing what went wrong and try to put forth one design that helps eliminate all of the observed problems, giving you a user domain model flexible enough to handle the changing nature of whatever business you're in.
Users Are Not Roles. Users Have Roles.
Instead of trying to model a type hierarchy for your users, consider instead that a user is simply someone who uses your system, plain and simple. Typically, users have things like usernames, passwords, email addresses, and the like. Frequently, users are tied to permissions via roles (e.g., "an administrator can do function X"). It is this role-based solution which provides the flexibility -- whether you use it for security and permissions or not is irrelevant. Let's examine.A role is a set of motivations for a given user. And, as with any good actor, users can play many different roles -- frequently at the same time. This important distinction leads us to aggregate roles into a user rather than attempt to model by hierarchy.
Once you aggregate the roles, you begin to see the power of the solution. Now you can create hierarchical models of roles to support your business needs, and you can have a user play as many (or few) roles as necessary.
In Figure 2 above, we've refactored the inflexible design from the previous post. A user can now have several different Roles and a hierarchy of Role provides us with ample flexibility to use inheritance the way it was meant to be used -- to pass common attributes and methods along to children. Each Role implementor provides domain-specific information making it easy to use and clear to read.
What's even more fantastic about this methodology is something I didn't even bother to touch on in the previous post. Previously, you would have broken into tears trying to come up with a flexible database schema to go with your domain model. But with this model, you can easily map a role to a table and use an intermediate mapping to go from users to their roles. Presto, a flexible domain model and persistence strategy to boot.
On a final note, this model also meshes very well with security frameworks, where the Role implementations are likely to map to permissions -- another side benefit with no additional work.