Modeling people and organizations: Party generalization
Classes for people and organizations are often modeled incorrectly in object models. These faults cause heavy implementation problems. In this posting I will analyze some legacy models, introduce class Party as generalization of people and organizations and provide some implementation details. Information here is based on my own experience, like always.
Legacy models
Pretty often we can see something like these in legacy object models. I added associations with address classes too because this way you get better picture of problems I will describe.
Both of these models introduce some pretty bad problems when we are going to implement these models. Major problem is that we have two classes – Person and Company – and we want to use them in same contexts. Their separation introduces additional association on the left model and one additional class with association on the right model. Let’s see one scenarios where problems occur.
Handling people and companies as one is pretty common requirement. We may sell products to persons and companies, so they can be both our customers. We need simple dropdown list on order form to let sales person select customer. Considering the models above this is actually easy thing to do (although the solution looks awful to me):
SELECT
'P' + CAST(id AS varchar(8)) AS customer_id,
last_name + ', ' + first_name AS name
FROM
person
UNION
SELECT
'C' + CAST(id AS VARCHAR(8)) AS customer_id,
name
FROM
company
ORDER BY
name
This SQL contains something I don’t like. Take a look at these ID-s. These ID-s must contain something we can use when we save customer data. We have to know which table has this ID so we can ask person or company and then assign it to appropriate property of order.
- Get customer ID
- Determine customer type
- If customer is Person
- Extract ID
- Ask Person with specified ID
- Assign it to Person property of Order
- If customer is Company
- Extract ID
- Ask Company with specified ID
- Assign it to Company property of Order
- If customer is Person
- Save Order
And even worse – if we have entities like Invoice, Order and SupportCase then they all need two fields for customer data. One field for relation to Person table and another one for relation to Company table.
Introducing parties generalization
People and organizations can be handled as parties of different deals. Ordering, buying, signing a contract – all these activities are made by different parties. We can generalize people and organizations using class named Party. You can read more about it and other related models from The Data Model Resource Book – Universal Data Models. I suggest you to buy these books because they help you a lot when you analyze or model your applications.
Here you can see class diagram where Person and Category are generalized using Party.
Class Party helps us a lot. All associations that are common for people and organizations we can define between Party and other classes. This way we can handle people and organizations as same.
On the code level we have to do some tricks. We define DisplayName as abstract property in Party. Person and Company both have override for this property. Setter of overridden properties is empty because we don’t want to let users change DisplayName manually.
public abstract class Party
{
public int Id { get; set; }
public abstract string DisplayName { get; internal set; }
public string Address { get; set; }
}
public class Company : Party
{
public string Name { get; set; }
public override string DisplayName
{
get { return Name; }
internal set { }
}
}
public class Person : Party
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override string DisplayName
{
get { return LastName + ", " + FirstName; }
internal set { }
}
}
Internal set method is needed by some ORM-s because otherwise they consider DisplayName as read-only property and doesn’t update it in database if it changes.
Parties in database
On database level we have multiple approaches to keep data. We can go with three tables like shown on the following diagram. It makes sense when company and person tables have many fields that are not taken to party table.
One thing more – we can see that display name is field of table party. Why? I have found it very convenient if we can see the name of party when we need to manage data in tables manually. DBA is able to solve different issues faster when he or she has all the information that describes rows in table. f course, not using the display name in database level is also allowed. Just make your decision considering your current context and needs.
NB! The model above needs special care as inserts and deletes are going to two different tables. Make sure you use database transactions so all data operations made on these tables either succeed or fail all together. Otherwise you may end up with broken data that makes ORM throw exceptions.
When using Entity Framework Core we will have different database structure. Using simple database context I let Entity Framework Core to create database and all tables.
public class PartiesDbContext : DbContext
{
public PartiesDbContext(DbContextOptions<PartiesDbContext> options) : base(options)
{
}
public DbSet<Party> Parties { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<Company> Company { get; set; }
}
The result is just one table called Parties. This is because Party is abstract class and cannot be instantiated.
When inheritance hierarchy is held in one table there must be some way to understand entity type. For this ORM-s use discriminator field. Entity Framework Core creates nvarchar(max) type discriminator field and stores there class name.
Even with one table per inheritance tree we need display name field to fill dropdowns and autocomplete boxes in UI of application.
Trying out parties generalization
To try out party generalization we can use dummy console application. Here’s the code to print out list of parties.
var parties = new List<Party>
{
new Company { Name = "The Very Big Corporation of America"},
new Person { FirstName = "John", LastName = "Doe"},
new Person { FirstName = "Jane", LastName = "Doe" }
};
foreach(var party in parties)
{
Console.WriteLine(party.DisplayName);
}
And here’s the result on console window.
Using display name property we got nice list of parties printed out.
Wrapping up
Instead of building custom multi-purpose tables or going with other clumsy approaches we must consider using parties generalization to have clean and flexible class and database models. Parties generalization greatly helps us to have solid model to support modelling people and organizations. Also we can more easily model relationships between parties. Still the main point of this pattern is to get people and organizations under same roof.
Why the concept “Party” wouldn’t LegalEntity or maybe something like Agent be more appropriate ?
Look at some more models at
http://edemocrazy.sourceforge.net/apidocs/org/directdemocracyportal/democracy/model/world/package-summary.html
based on
http://edemocrazy.sourceforge.net/apidocs/org/directdemocracyportal/democracy/model/core/package-summary.html
A little OT, but what software was used to generate the class diagrams?
Bob, It looks like the entity designer in VS2008SP1 used to create edmx files.
Bob, Chris is right. These images are made width Visual Studio 2008 entity model designer. :)
I’ve always used “Entity” as a top-level organizer for such things; more general-purpose. I guess I’m a little surprised that people are excited by this since it’s hardly a new idea: inheritance.
I agree with you, Dave. It is not an new idea. But still there are a lot of systems that use different hacks and pretty weird and complex solutions instead of solution I offered here.
There are even analysts and software designers who stuck in this problem. So I think it is good idea to target some posts to people who don’t generalize and inherit things so smoothly than we do. :)
Thanks Jordan! :)
Sometimes trackbacks are not published immediately because of caching. But they work.
How would this pattern handle organisation data such as person x works in team a in department b for group c in company d?
Can class party handle that set up?
Many thanks
John
Hi,
The term “Party” is commonly used to describe the ideal database structure for CRM systems designed for the services industry. It is also referred to as a “Party Data Model”. Many CRM systems are based on product classes or account classes. Party class however shifts the focus to the individual as the universe center, and enables the storing of all other relevant relationships to the individual. This is common within products such as enCentral from enVisual and is ideal for managing customer or client service organizations.
Pingback:Using NHibernate order-by attribute to sort names history | Gunnar Peipman - Programming Blog
Pingback:Modeling people and organizations: Employees