Managing 1:N and N:N Object Relations
When you design a domain model you normally have lots of 1:n and n:n relations. Many developers are quite familiar how to translate such relations into a relational model. But how do you translate such relations into an object-model? There’s no hard guideline for that. In this post I explain what I usually do.
The Domain Model
For the examples I use a this small domain-model. Our application has authors, which write posts. An author has multiple posts and a post has only one author. Each post can have multiple tags and a tag is used for multiple posts.
The Possibilities
The majority of object oriented language give two tools for this job, references and collections. And therefore a lot of possibilities.
- Referencing from the ‘child’. In our example the post would have a reference to the author.
- Referencing from the ‘parent’. In our example the author would have a collection of all its posts.
- Bidirectional reference, in our example the post has a reference to the author and the author a collection of his posts.
And in the n:n scenario all this possibilities above also apply. Just that each participant uses collections.
Navigation-Paths Dictate
Which one of the 3 possibilities do you pick? The main criteria is how you’re application navigates through the data. For example when you’re accessing the blog posts and only want to know who has written this post, you add a reference to the author. When your always getting a post via its author, you add a collection of posts to the author. And when your application uses both navigation paths, you use a bidirectional relation.
The same applies for the post-tag relationship. When you only want to know which tag a post has, you add a collection to the post with the tags. When you want to know which post have a certain tag, you add a collection of all posts to the tag.
Bidirectional References: A “chicken or the egg” dilemma
As soon as you have bidirectional references, you cannot avoid a ‘chicken or the egg’-dilemma. You have to create one instance first and then later assign it. Sometimes it’s easy solvable by adding a factory-method to the parent-class. Like this: But often there’s no clear ‘parent’ or natural order. Especially in n:n relations. For this cases I use my special extension-methods. Let’s start with a simple class which represents the author and a post
Then I ‘upgrade’ the properties and methods with extension-methods to make them relation-aware. Basically on each property/method I also state what happens to the relationship-partner. Note that you shouldn’t expose the collections directly. Instead only expose it a read-only IEnumerable and provide additional, domain-specific manipulation-methods. Here’s the code Now I can assign, add or remove the object freely, and it’s ensured that the relation is in a consistent state
Of course this also works for n:n-relations. For example we start with the relation between tags and posts Again, we ‘upgrade’ the add and remove method for the relations
And oh wonder, we can freely add and remove and the relation is kept in a consistent state
Implementation of the Extension Methods
As you can imagine, the implementation isn’t pretty. Especially to avoid endless recursion, some hacks are required. I don’t want to go into detail here (maybe another time). You can take a look at the source yourself. Here it is: the extension, the tests, tuple class.
There a certainly cleaner ways to achieve something similar, like special relation-collections. But for my projects this solution is more than enough.
Conclusion
Managing object-relations can be very easy or can be pain. When you have reference in one direction it’s straight forward. But as soon as bidirectional references are required, it can be tricky.
Anyway, critic, tips for improvements or links to good articles on this topic are welcome.
- Games Outside the Box, Analog Computers, Read-Write-Costs etc.
- Fixing the Font for Github Gists
Pingback: db4o in the Real World - Using db4o on a web project | emphess .NET