views:

129

answers:

3

Let's say I'm modeling phone numbers. I have one entity for PhoneNumber, and one for Person. There's a link table that expresses the link (if any) between the PhoneNumber and Person. The link table also has a field for DisplayOrder.

When accessing my domain model, I have several Use Cases for viewing a Person.

  1. I can look at them without any PhoneNumber information.
  2. I can look at them for a specific PhoneNumber.
  3. I can look at them and all of their current (or past) PhoneNumbers.

I'm trying to model Person, not only for the standard CRUD operations, but for the (un)assignment of PhoneNumbers to a Person. I'm having trouble expressing the relationship between the two, especially with respects to the DisplayOrder property. I can think of several solutions but I'm not sure of which (if any) would be best.

  1. A PhoneNumberPerson class that has a Person and PhoneNumber property (most closely resembles database design)
  2. A PhoneCarryingPerson class that inherits from Person and has a PhoneNumber property.
  3. A PhoneNumber and/or PhoneNumbers property on Person (and vis-a-versa, a Person property on PhoneNumber)

What would be a good way to model this that makes sense from a domain model perspective? How do I avoid misplaced properties (DisplayOrder on Person) or conditionally populated properties?

+3  A: 

Option 1 has the most advantages since this is a many to many relationship. Each person will have a list of PhoneNumberPerson objects and each Phone Number will have a list of PhoneNumberPerson objetcs effectively creating two one to many relationships.

Managing the two one to many relationships will prove easier in the long run.

A person with no phone will then be a person whose PhoneNumberPerson list is empty. The inheritance option looks like a difficult one to maintain.

Additionally the PhoneNumberPerson class can carry information like the date that the person started using the phone and stopped using the phone so that it is easy to tell if it is a current phone or not.

Vincent Ramdhanie
+1 - modeling the Edge connecting Person and PhoneNumber as an Entity itself makes most sense because it allows you to add extra information to that Edge at any time.
Hightechrider
What sort of information would make more sense living on the edge, so to speak, than on one of the entities joined by that edge?
Jeff Sternal
I started implementing this, and I'm finding the time-slicing to be the more problematic part. I have to pass in a date to find the phone numbers, but at that point, the person stops being just a person, but rather a historical person. It's kind of weird, but works.
Greg
@Jeff Sternal Anything that describes the edge rather than the entity. Let us say that the carrier was important information, a person may have multiple phones from multiple carriers. Where should carrier information go? It does not describe the person, it does not describe the phone, it describes the relationship between the phone and the person. Thus, it goes on th edge.
Vincent Ramdhanie
I dunno Vincent - carriers aren't really related to numbers (or the relationship between a person and a phone number), since people often use the same number across multiple carriers (and sometimes use multiple numbers with the same carrier). I'd be inclined to materialize that as a distinct entity associated directly with Person. I still don't see a persuasive use-case for pushing attributes to an edge. Effective period data *sounds* promising, but in my experience, it's generally easier to model that as an attribute of the child entity.
Jeff Sternal
@Jeff Sternal If carrier (I don't know that this is the best example) becomes an attribute of Person, how do you represent that a person has multiple phones from multiple carriers? If it was an attribute of Phone, how do you represent that a phone may move from one carrier to another? Since a person has many phones, each one may be from a different carrier or the same carrier, then making the carrier an attribute of the PhoneNumberPerson class captures this. So if my understanding of carriers is wrong then its abad example, but the concept is valid.
Vincent Ramdhanie
Aha! I see: if a person can have multiple carriers and phones *at the same time*, carrier has to be a property of phone or vice versa. In a case like that, I would model one of those entities as the edge. My only gripe is that I would want it to **be** the edge, not an attribute of a synthetic intermediary class.
Jeff Sternal
+1  A: 

What would be a good way to model this that makes sense from a domain model perspective?

Your third option sounds the best, by virtue of its simplicity:

Person.PhoneNumbers // a list of phone numbers
PhoneNumber.Person  // references its parent

How do I avoid misplaced properties (DisplayOrder on Person) or conditionally populated properties?

Is DisplayOrder arbitrary, or is there some domain meaning embedded in it, such as the date it was replaced by a new phone number? If so, I'd change the attribute so it expresses that meaning. That is, don't store the display order in your database, store the information you need to construct the correct display order and let your views order them, perhaps using a strategy defined in your domain model. (For example, a StandardDisplayOrderStrategy might show 1) home numbers, 2) work numbers, then 3) mobile numbers.)

As for conditionally populated properties, consider populating them all, all the time.

Jeff Sternal
About DisplayOrder. Imagine a cell phone where you look at a contact (Person). The user might want to reorder the list of that Person's phone numbers. The DisplayOrder would store that value. *the problem's not really about phone numbers, so if the analogy starts to break down there, I'm sorry. Blame NDA. :(*
Greg
Aha, blasted NDA! If the display order attribute captures a intelligible meaning within the domain I'd just include it on the entity - but perhaps it's too hard to see through the veil of secrecy here. ;)
Jeff Sternal
+1  A: 

Make the Join-Table a separate class, e.g. PersonalPhoneNumber:

  • PersonalPhoneNumber has two fields: One Person and one PhoneNumber
  • Person has a list (java.util.List) of *PersonalPhoneNumber*s
  • the ordering of the list is what you wanted to express as DisplayOrder
  • PhoneNumber can also have a set of Persons
Willi