Employee Types

One of the most common "textbook examples" used to illustrate various business programming concepts. It usually involves creating a taxonomy of employees for a human resources and/or payroll system.

However, some think that "types" are a bit misleading because there are multiple orthogonal possibilities. One can divide by management status or level, by being part-time or full time, by being federally exempt or non-exempt (closely mirrors, but not identical to management status), and so forth. There is no one right taxonomy, unless perhaps one makes a full combinatorial explosion (CartesianProduct) of every factor combination. Some of us don't find that approach very useful, especially since the employment laws and HR needs are often changing the nature of employee classification and encoding systems. The removal, change, or addition of factors can throw the taxonomy tree for a loop (figuratively speaking). If each factor is treated independently, then changes to specific factors will mostly only affect areas that use that given factor. With a global taxonomy tree, ANY change to it may ripple throughout the system. -- top


The need to classify employees, of different types, is an interesting categorization problem - for reasons both technical and non-technical. Among the reasons:

 abstract class Employee {
     public EmployeeId? get_id ()
     public Date get_hire_date ()
     public Money get_base_salary() {... /* maybe abstract */}
     public Manager boss() { ... /* returns the boss */ }
     public void fire() {... }

public void promote(...) {} // and so on }

class Programmer extends Employee { public Language[] get_languages_known () public Project[] get_current_projects () }

class Manager extends Employee { public Employee [] get_direct_reports () { } public Employee [] get_all_reports() { } public Money get_department_budget() {} }

What happens when you wish to promote a programmer to a manager? One way is to use TypeMigration; and evolve the type of the employee from Programmer to Manager. This causes all sorts of heck to break loose with languages with StaticTyping--in many, you simply cannot do it. Another way is to create a new Manager object which replaces the old Programmer object throughout the system (the promote() method is not a mutator; instead it returns a new Employee object or a subtype); something else (a RelationalDatabase or some other ManagedCollection) makes sure that the old Programmer type is no longer used.

Others may throw up their hands at this point and abandon subtyping as a means of classifying employees.

The most common solution I've seen (and used) is to give each role its own RoleObject and let an employee belong to multiple roles. When a programmer becomes a manager the employee loses its programmer role and gains a manager role. -- EricHodges

In smaller companies, it is not uncommon to be both. For example, suppose during the dot-com boom it was decreed that all programmers were to get a temporary 10 percent raise. (A person who is both a manager and a programmer may qualify also it would seem.) I often find that the granularity of new features tends toward being smaller than the original types/categories allowed for. At first, maybe only managers get bonuses and we can tie bonus to that. But often, "special" circumstances are introduced so that the determination for a given feature grows more complicated or multi-faceted. Thus, the coupling between a given type/category and feature should be loose and flexible.

Either way, multiple independent "roles" is basically set theory. If you track them in the RDBMS, then it is easier to report on, query, and share that info with other languages and tools. Role classes are simply reinventing a NavigationalDatabase in a hard-wired kind of way. I can't think of a good reason to model these with OOP classes. I suppose somebody will claim it helps "manage behavior" of roles. I would like to see a specific example, as I am skeptical of these behavior claims (surprise surprise). See AccessControlList for examples of relational role modeling. -- top

The standard relational approach is to have a table for all employees; the records of which contain that information common to all employees (in our example - base salary, date of hire, employee ID) and that can't be determined elsewhere. (The employee's boss, representing a relationship between two entities, may be handled below).

Hard-wiring classifications into code (such as classes) is often a code smell unless you are truly certain they are stable. It often creates programmer job-security but not change-friendly software. Related: PutClassificationsIntoMetaData.

Separate tables then exist for the various roles employees take on. A Programmer table would list that information specific to programmers (languages known, projects being worked on, etc.) Similarly, a manager table would contain information that pertains to managers - the department, the reserved parking space, etc. :)

I would be hesitant to have tables such as "programmer" and "manager". For the parking space example, perhaps special non-manager employees get parking spaces also, such as "Employee of the Month". I would leave parking spaces in an Employee table. As far as programmer info, perhaps have a Skills and/or hasSkills table which is used by all professions. Otherwise there may be problems with skills that overlap.

Regarding the "boss" and "direct_reports" methods in the OO example; there are several ways to handle this. In a fully normalized database, this would be stored OnceAndOnlyOnce; the bidirectional pointers between each Employee and his Manager don't occur. Since it's a one-to-many relationship (for the purposes of this example); one approach would be to store a "boss" attribute in each Employee record. If the boss wants to know who works for him (directly, or the transitive closure of his/her reports), the query engine goes and finds it for him. The other approach which might be taken is to have a separate WorksFor? table, listing pairs of employees and managers. (The employee is the PrimaryKey in such a table {Primary?}). Were employees to have multiple bosses ("dotted line" reports, for example), such a table would be the only approach.

Storing a vector/list/other sequence of "direct reports" in the Manager table wouldn't work. Although tables can be attributes in the RelationalModel, most RDBMS's don't allow this.

This sounds just like the typical OO solution.

{Assuming multiple potential bosses, what is wrong with a many-to-many table such as "worksFor" above?}

I have seen this kind of pattern for other entities in large organizations, not just employees. It is tough to clean up because there are simply lots of combinations in the business rules. Any larger-scale abstraction is generally arbitrary and likely not permanent. Thus, patterns can be isolated, but one might be wasting their time by doing it.

Things are sometimes divided into sub-types because some kind of boundary, ANY boundary, is needed to divide up the labor and code in order to keep sane. However, each subtype still has a lot of duplication because there are too many factors that are too different or irrelevant across subtypes. Subtyping is thus NOT used for the purpose of reuse and DeltaIsolation, but to simply break big problems up into smaller projects for personal assignment and code management. The result is often ugly and full of duplication and I have no easy solution. I study and ponder the patterns looking for a magic road to simplicity, but then find exceptions to the rule in the candidate abstractions.

A dirty compromise appears to be creating a single level of subtypes on the factor or factor sets which seem to be the strongest factors of influence, and then use look-up tables or ControlTables with a CartesianJoin-like pattern for the rest of the factors within those subtypes. Identifying the "strongest factors" is often an exercise in marginality and may pit one department against another because relevance is relative.

One tip is to create "optional abstractions". These are simplification options that can be applied if a case fits, but that can be abandoned with relative ease if the situation changes. -- top

PayrollExample is a runnable code example of an alternative over subtyping employees (for Payroll purposes). It basically uses the FeatureBuffetModel.

See also: ThereAreNoTypes, SetsAndPolymorphism (example), ContactAndAddressModels, PayrollExample

CategoryExample, CategoryBusinessDomain

EditText of this page (last edited December 20, 2012) or FindPage with title or text search