Written by Vaughn Vernon
Solution Pattern
Fundamental Identity
Defines the data structure and persistence of the identity of each user of a system. As a side-effect of persistence this pattern also defines how identities are accessed.

Background
Systems and applications must store information about its users. While different business systems will have different requirements for the kind and amount of data stored for a given set of users, there is always a fundamental, underlying definition of those requirements. A clear set of requirements is needed to establish what information is gathered for each user and how it is to be stored.
One major decision must be made. Should the identities of users be captured into a traditional rational database, or should they be hosted on a directory system?
Value and Benefits
The users of a business system are generally the reason the system exists in the first place. Having a well defined, centralized definition of structure, security, and componentry is invaluable. Preventing the evolutionary growth of identity silos should be a key goal of every IT organization.
For businesses that collect user information, this pattern transcends developer boundaries. Whether they consciously realize it or not, nearly every system stakeholder will be more or less aware of the kinds of information that is maintained about its users. The better understood this is by architects and developers the more consistent its use will be, and the more valuable it can be to the business. The Fundamental Identity pattern should become as familiar to you as where you keep your wallet and keys, and as certain as what you like on your pizza.
The underlying technology that maintains your user data will make it either easier or more difficult to add, access, manage, dispose of, and even refactor and share its entries. So choosing the correct backing technology has enormous benefits that may not be obvious at first. Reference the other solution patterns within Identity and Access Management (page #) to understand why the correct backing technology matters so much. But to say the least, identity and access management technology decisions are right up there with programming language, database, and application servers. To believe differently may put you on a bumpy and winding road, when what you really wanted was smooth straightaway.
While the focus of this pattern is on information, structure, and persistence, there is value in introducing a fair share of architecture and design as well. These important considerations will help make the information more accessible and maintainable over time.
Putting It to Work
Here I will focus on the primary concerns of this pattern: information and structure. This is followed by examples, which present a flexible realization of this solution pattern that can be used within many organizations.
Information and Structure Revisited
As indicated in its overarching EBP, Fundamental Identity dictates that you first identify your users and the information you will collect about them. This information will depend on the kind of system you are developing. The dataset may be quite simplistic or moderately complex. Primarily you will support two kinds of fundamental identities; one for persons and one for systems. The following diagram provides a fairly complete structural domain model for this pattern. Refer back to the complex identity tree in the AccessManagement(page #) pattern for a textual rendition of the UML domain model found here. Also note that the User object is part of the User (pg#) pattern, but is included here for clarity. Grouping relationships are not shown at all in this diagram:

The two primary types of users are showcased in this domain model, persons and systems. Both have a natural association with the User class, which contains the authentication credentials for the particular kind of user.
My definition of a System has a very simplistic fundamental identity. It has a text-based name, description, and location, as well as a netAddress. The netAddress is most likely an IP address.
A Person may have a much more complex fundamental identity. First of all there is a basic notion of a Person. Our basic Person has name, date of birth, and unique identification related data. The name and date of birth information is rather obvious, but the uniquePersonIdentifier may not be. In the United States this could be the person’s Social Security Number (SSN). In other countries it may or may not have any meaning, or it could have application domain specific meaning. You can enhance our Person class to suite your needs.
If you are looking for more specific kinds of users, notice the types to the right of Person. The three classes specialize the Person class to address the three kinds of human users that were identified above, namely customers, employees, and partners. A CustomerPerson includes a specific organizationName, while EmployeePerson does not. That is because you know what your organization is named, or you know that your employees are associated with some sort of organizational entity—see Identity Grouping (page #). The same goes for PartnerPerson, except that those would be grouped under a partner organization. If you decide not to use groupings and you have multiple organizations, you may want to move organizationName and related attributes to the Person class, but this is not recommended practice. Groupings are a much more flexible way of dealing with complex organizations.
Next look at the contact information. This is a container composition. In other words, each Person has only one dedicated contact information object, but the ContactInformation object may contain zero or more of each specific contact information type.
Although the domain model provides some helpful ornamentation defining multiplicity, it is inappropriate for me to be dogmatic about the actual numbers of objects that you collect for a given Person type. For example, while you will probably want to have an email address for all customers, you may not require any more detailed personal information. To be sure, most external users would prefer not to give up their telephone number unless they absolutely must (something that your marketing department may insist is an absolute must). Furthermore, while companies must know the SSN for every US-based employee, European companies don’t need such data for EU citizens (currently). Additionally, you’ll be hard pressed to squeeze the SSN out of anyone unless you have the right to demand it.
The point is that each Person subclass will need to provide its own validation based on the realities of your implementation. The CustomerPerson validation may allow telephone numbers to be optional contact information, while EmployeePerson and PartnerPerson will consider them mandatory. Your custom validation must also ensure that there is no more than one primary EmailAddress, PostalAddress, and TelephoneNumber.
Examples
Here I provide snippets of a Java-based example of Fundamental Identity. You should refer back to the above diagrams for class and component views of the following source code artifacts.
One of the focuses of this solution pattern is the domain model and corresponding domain objects. As highlighted in the diagrams the domain objects are concerned with the structure and access of the various data properties of person identities and their closely associated contact information.
Following are listings of the key data elements. To start, here is interface PersonIF and the Person domain object class:
package com.jubatus.business.domain.person;
import com.jubatus.business.domain.DirectoryDomainObjectIF;
public interface PersonIF extends DirectoryDomainObjectIF {
public ContactInformationIF getContactInformation();
public String getFirstName();
public String getLastName();
public String getUniquePersonIdentifier();
public void setContactInformation(ContactInformationIF aContactInfo);
public void setFirstName(String aFirstName);
public void setLastName(String aLastName);
public void setUniquePersonIdentifier(String aUniqueId);
}
package com.jubatus.business.domain.person;
import com.jubatus.business.service.directory.DirectorySessionIF;
public class Person implements PersonIF {
private ContactInformationIF contactInformation;
private DirectorySessionIF directorySession;
private String firstName;
private String lastName;
private String uniquePersonIdentifier;
public Person() {
super();
}
public ContactInformationIF getContactInformation() {
return contactInformation;
}
public DirectorySessionIF getDirectorySession() {
if (directorySession == null) {
directorySession = new PersonDirectorySession();
}
return directorySession;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getUniquePersonIdentifier() {
return uniquePersonIdentifier;
}
public void setContactInformation(ContactInformationIF aContactInfo) {
contactInformation = aContactInfo;
}
public void setFirstName(String aFirstName) {
firstName = aFirstName;
}
public void setLastName(String aLastName) {
lastName = aLastName;
}
public void setUniquePersonIdentifier(String aUniqueId) {
uniquePersonIdentifier = aUniqueId;
}
}
The Person’s contact information is managed by following interface and class. Note that because I use object class inetOrgPerson I am not able to support multiple contact information objects in a single entry. For simplicity I also skip the use of a postal address even though it is supported by inetOrgPerson.
package com.jubatus.business.domain.person;
import com.jubatus.business.domain.DirectoryDomainObjectIF;
public interface ContactInformationIF extends DirectoryDomainObjectIF {
public String getEmailAddress();
public String getTelephoneNumber();
public void setEmailAddress(String anEmailAddress);
public void setTelephoneNumber(String aTelephoneNumber);
}
package com.jubatus.business.domain.person;
import com.jubatus.business.service.directory.DirectorySessionIF;
public class ContactInformation implements ContactInformationIF {
private String emailAddress;
private String telephoneNumber;
public ContactInformation() {
super();
}
public String getEmailAddress() {
return emailAddress;
}
public String getTelephoneNumber() {
return telephoneNumber;
}
public void setEmailAddress(String anEmailAddress) {
emailAddress = anEmailAddress;
}
public void setTelephoneNumber(String aTelephoneNumber) {
telephoneNumber = aTelephoneNumber;
}
public DirectorySessionIF getDirectorySession() {
return null; // PersonDirectorySession takes care of me
}
}
Class Person delegates persistence responsibilities to its DirectorySessionIF implementer, PersonDirectorySession:
package com.jubatus.business.domain.person;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import com.jubatus.business.domain.DirectoryDomainObjectIF;
import com.jubatus.business.service.directory.DirectoryException;
import com.jubatus.business.service.directory.DirectoryService;
import com.jubatus.business.service.directory.DirectorySessionIF;
class PersonDirectorySession implements DirectorySessionIF {
private static final String GIVEN_NAME = "givenName";
private static final String SURNAME = "sn";
private static final String PHONE = "telephoneNumber";
private static final String MAIL = "mail";
private static final String UNIQUE_IDENTIFIER = "uid";
private String parentContext;
PersonDirectorySession() {
super();
}
public void save(DirectoryDomainObjectIF anObject)
throws DirectoryException {
if (anObject == null) {
throw new NullPointerException("PersonIF must not be null");
}
PersonIF tempPerson = (PersonIF) anObject;
ContactInformationIF tempContactInfo =
tempPerson.getContactInformation();
if (tempContactInfo == null) {
throw new NullPointerException(
"ContactInformationIF must not be null");
}
DirectoryService tempDirService =
DirectoryService.getInstance();
String tempFullName = tempPerson.getFirstName()
+ " " + tempPerson.getLastName();
String tempContext = tempDirService.getPersonContext(
this.getParentContext(), tempFullName);
Attributes tempAttrs = new BasicAttributes();
tempAttrs.put(tempDirService.getPersonObjectClasses());
tempAttrs.put(new BasicAttribute(GIVEN_NAME, tempPerson.getFirstName()));
tempAttrs.put(new BasicAttribute(SURNAME, tempPerson.getLastName()));
tempAttrs.put(new BasicAttribute(PHONE,
tempPerson.getContactInformation().getTelephoneNumber()));
tempAttrs.put(new BasicAttribute(MAIL,
tempPerson.getContactInformation().getEmailAddress()));
tempAttrs.put(new BasicAttribute(UNIQUE_IDENTIFIER,
tempPerson.getUniquePersonIdentifier()));
tempDirService.createSubcontext(tempContext, tempAttrs);
}
public void setParentContext(String aContext) {
parentContext = aContext;
}
public void update(DirectoryDomainObjectIF anObject)
throws DirectoryException {
}
protected String getParentContext() {
return parentContext;
}
}
Viewing my custom OpenLDAP configuration file, slapd.conf, you can see that the inetOrgPerson schema must be added in manually. Before including inetorgperson.schema, however, you must also include cosine.schema, which the other depends on.
include /bin/openldap/schema/core.schema
include /bin/openldap/schema/cosine.schema
include /bin/openldap/schema/inetorgperson.schema
. . .
A supporting feature of my example is the Remote Façade—a remote service that clients use to access identity objects. Here is the definition of the EJB home and session bean interfaces for IdentityManager:
public interface IdentityManagerHome extends EJBHome
{
public IdentityManager create() throws RemoteException, CreateException;
}
Since we implemented the façade as a stateless session bean the home interface has a single, no-argument factory method. The remote interface on the other hand provides for a bit more discussion:
public interface IdentityManager extends EJBObject
{
public PersonVO authenticate(UserIF person, String key)
throws RemoteException, NamingException,
PersonNotValidatedException, PasswordFailedException;
public void register(PersonIF person, String orgUnitKey)
throws RemoteException, NamingException;
public void validate(PersonIF person, String orgUnitKey)
throws RemoteException, NamingException;
}
Clients use the register() method to register a new identity with the directory service. After the person and user are registered, the individual must follow up by validating their registration. Our registration process sends a confirmation email to the registering person. In turn the email receiver clicks a link in the email, which causes the confirmation process to be completed. Eventually the Java web client invokes the validate() method, completing the registration process.
When a registered user attempts to sign on to the application, the web client uses the authenticate() method to authenticate the user. While these three methods are discussed in detail in the Registration (page#) and Sign On (page#) solution patterns, noting their use here is important because they all use or support the Fundamental Identity pattern in some way.
The IdentityManager uses the Application Service [CoreJ2EE] pattern to house its core business logic code:
com.jubatus.business.service.IdentityManager.IdentityManagerBean
com.jubatus.business.service.IdentityManager.RegistrationCoordinator
com.jubatus.business.service.IdentityManager.SignOnCoordinator
To trace the initial storage of this pattern’s data, start with the registration GUI and trace the logic through every tier until you reach DirectoryService interaction with JNDI:
src/jsp/ebp/register.jsp
Consequences
You will find tradeoffs among the following competing forces within the Fundamental Identity solution pattern.
-
Directory Service or Database: You may have little choice here if your persistent storage mechanism is deeply ingrained in your enterprise. If possible use a service that specializes in identity and access management. That spells LDAP.
-
Set Reasonable Limits: It will help if you clearly identify what you really need in your solution and limit your implementation accordingly. If you don’t need multiple levels of contact information, for example, then only support the primary ones. You can always add complexity later as dictated by increasingly demanding requirements, if and when they arise. This competing force includes implementing enterprise access only when necessary. If you can support your foreseeable short- and long-term needs without the use of enterprise components (EJB), then don’t introduce the added complexity.
-
Fundamental, But Not Too Fundamental: Actually you may decide not to consume this pattern if all you need are simple users. Therefore, the User (page#) pattern may suffice. However, the above Directory Service or Database competing force may play in before simplicity.
Related Patterns
The following solution patterns may be used in conjunction with or instead of the Fundamental Identity pattern.
-
User (page #): While you may not absolutely need the Fundamental Identity pattern, you will always make use of the User pattern when you have users signing on to your system.
-
Registration (page #): Use this pattern to gather the data that gets captured into the Fundamental Identity storage.
-
Sign On (page #): This pattern uses the Fundamental Identity data to assist the user in signing on to the system.
Frameworks and Tools
-
OpenLDAP and ActiveDirectory: Especially for experimenting with Fundamental Identity we make use of freely and commercially available tools such as OpenLDAP and ActiveDirectory for storing and retrieving user identity and related information.
-
AK BK LDAP Schema Viewer: http://ldap.akbkhome.com/objectclasstree.htm