Someone creates a new document | V The document goes a particular supervisor determined by the topic of the document. Status: 'pending approval'. / \ / \ The document gets rejected The document gets approved and returns to the workspace and moves to the editors' of the author for more work. group workspace. Status: 'pending approval'. Status: 'approved'. / / Document moves from editors' group workspace to individual's workspace. Status: 'under editorial review'. And several more options below, but you get the idea I hope. #### Ticket::new(String group, String username, String documentID); # other values will come from the db Ticket::save; Ticket::move(String direction, int steps); # e.g., forward if okayed, # back if not okay, sometimes even # back to the beginning. #### package Track; our %switch = (); # to be filled in by subclasses. sub new { my ($class) = @_; # don't have any args in mind yet. my $this = bless({}, $class); return $this->init(); } sub move { AbstractMethod->throw("Not implemented in this class\n"); } # raise exception unless # implemented by a subclass package BasicTrack; use base Track; sub init { my ($this) = @_; # Add anonymous subroutines to the hash of possible combinations # of status and location. I.e., if the status is 'pending # approval' while the document is sitting in the workspace of a # supervisor, and 'forward' is the command issued to Track::move, # then the subroutine ought to set the status to 'approved', # change group to 'editors'. Other subclasses can inherit # or override these as necessary, to avoid duplication of behavior. $Track::switch{case1} = sub { # do the stuff }; } sub move { my ($this, $arg) = @_; # complicated set of switch statements to figure # out which subroutine in %Track::switch to call. # This would be called from Ticket::move most likely. }