Fluent Interface for System.Identity – Party Implementation (Part 1)
This latest post on developing a fluent interface for the Oslo System.Identity schema will start an implementation of the Party class. Per the architectural specifications for the fluent interface, the Party class will adhere to the Law of Demeter and “Tell, Don’t Ask”. As the Party class is large, and important to the System.Identity model, I’ll tackle Party in a couple of different posts.
Party is Central to System.Identity
The System.Identity schema is built around a particular design pattern commonly referred to as the “Party Model”. The Party Model is a powerful solution to a common problem found in software – how do you handle the complex relationships that exist between the actors in a system – especially where actors can be both physical and logical participants. This concept is illustrated in the MSDN documentation on System.Identity’s Party entity:
- “The directory stores information about digital subjects and resources that have relationships. Different kinds of digital subjects typically share many of the same characteristics – while being different. The System.Identity schema provides a single mechanism for defining all the different kinds of things present in the directory –represented through entities called Kinds. The System.Identity schema represents the common aspect of all digital subjects through an entity called a Party.”
The importance of Party to System.Identity is further illustrated in the following UML representation of part of the System.Identity schema. The UML clearly illustrates the centricity of Party to the System.Identity design:
As the diagram above illustrates, Party is associated with almost every other entity in the System.Identity schema. This makes designing a fluent interface for Party an interesting design exercise.
NOTE – For those devotess of Domain-Driven Design (DDD) I will be purposefully ignoring the ORM implications of Party (specifically Aggregates) in terms of Party methods, although use of the Law of Demeter and “Tell, Don’t Ask” produces code that is very conducive to “modern” domain model persistence designs. I’ve been toying with the idea of writing a series to reactoring the fluent interface to adhere to DDD and leverage NHibernate, but that might be a while down the road.
The Party Basics
As with the fluent interface implementation of the Kind class, Party’s code will exhibit a certain shape given adherence to “Tell, Don’t Ask”. The following snippet shows the first pass on Party’s code:
1: public class Party : SystemIdentityBase
2: {
3: public string Name { get; private set; }
4: public string DisplayName { get; private set; }
5: public string YomiDisplayName { get; private set; }
6: public string DescriptiveInformation { get; private set; }
7: private Kind Kind { get; set; }
8: private Kind PrimaryKind { get; set; }
9: private List<Role> Roles { get; set; }
10:
11: public Party ( string name, string displayName, string yomiDisplayName, string descriptiveInformation, Kind kind, Kind primaryKind )
12: {
13: ChangeName ( name );
14: ChangeDisplayName ( name );
15: ChangeYomiDisplayName ( yomiDisplayName );
16: ChangeDescription ( descriptiveInformation );
17: ChangeKind ( kind );
18: ChangePrimaryKind ( primaryKind );
19:
20: Roles = new List<Role> ();
21: }
22:
23: public void ChangeName ( string name )
24: {
25: Name = name;
26: }
27:
28: public void ChangeDisplayName ( string displayName )
29: {
30: DisplayName = displayName;
31: }
32:
33: public void ChangeYomiDisplayName ( string yomiDisplayName )
34: {
35: YomiDisplayName = yomiDisplayName;
36: }
37:
38: public void ChangeDescription ( string descriptiveInformation )
39: {
40: DescriptiveInformation = descriptiveInformation;
41: }
42:
43: public bool IsOfKind ( Kind kind )
44: {
45: return Kind.Equals ( kind );
46: }
47:
48: public void ChangeKind ( Kind kind )
49: {
50: Kind = kind;
51: }
52:
53: public bool IsOfPrimaryKind ( Kind primaryKind )
54: {
55: return PrimaryKind.Equals ( primaryKind );
56: }
57:
58: public void ChangePrimaryKind ( Kind primaryKind )
59: {
60: PrimaryKind = primaryKind;
61: }
62: }
In addition to the basic aspects of Party (i.e., attributes of Party), the code snippet above contains a private automatic property for the collection of Roles a Party may play. The design of Party’s interface with respect to the Roles collection will be the subject of the rest of this post.
NOTE – The remainder of this series will use simple generic Lists to represent the various collections in the System.Identity schema. The use of Lists is not necessarily optimal, so the fluent interface offers GetHashCode() implementations for those applications that require better than O(n) performance.
Telling a Party About its Roles
In the context of “Tell, Don’t Ask” we can think about the Party methods in terms of the specific domain that System.Identity addresses. For example, we could think of the Party interface as a means of facilitating the following scenarios:
- Dave Langer accepts employment with Microsoft Corporation
- Determining if Dave Langer is employed
- Determining if Dave Langer was employed by Microsoft Corporation from MM/DD/YYYY to MM/DD/YYYY
- Dave Langer terminates his employment with Microsoft Corporation
- Dave Langer is no longer an Architect on any projects at Microsoft Corporation
First in the above list is providing a method for a Party to assume a new Role. The method implementation is straightforward as illustrated by the following code snippet:
1: public void AssumeRole ( Role role )
2: {
3: if ( !Roles.Contains ( role ) )
4: {
5: Roles.Add ( role );
6: }
7: }
The next scenario is where “Tell, Don’t Ask” really shines in producing code that just has an OO “feel” to it. The Party class’ public interface will provide two methods for determining what Roles a Party might play:
1: public bool PlaysRole ( Role role )
2: {
3: return Roles.Contains ( role );
4: }
5:
6: public bool PlaysRole ( Predicate<Role> matchingSpecification )
7: {
8: Role role = Roles.Find ( matchingSpecification );
9:
10: if ( role == null )
11: {
12: return false;
13: }
14:
15: return true;
16: }
The first method in the code snippet above addresses the scenario of “tell me if Dave Langer is employed”, while the second method supports the scenario of “tell me if Dave Langer was employed by Microsoft Corporation from MM/DD/YYYY to MM/DD/YYYY”.
Lastly, Party’s interface will provide some OO goodness for telling a Party to stop playing a Role:
1: public void StopPlayingRole ( Role role )
2: {
3: Role roleToStopPlaying = Roles [ Roles.IndexOf ( role ) ];
4:
5: roleToStopPlaying.ChangeEndDate ( DateTime.Now );
6: }
7:
8: public void StopPlayingRoles ( Predicate<Role> matchingSpecficiation )
9: {
10: List<Role> rolesToStopPlaying = Roles.FindAll ( matchingSpecficiation );
11: DateTime timeStamp = DateTime.Now;
12:
13: foreach ( Role role in rolesToStopPlaying )
14: {
15: role.ChangeEndDate ( timeStamp );
16: }
17: }
As System.Identity supports the concept of “logical deletes” for Roles via start and end dates, the methods above don’t actually remove Role objects from the collection. The first method in the code snippet above implements the scenario of “Dave Langer quit work at Microsoft” and the second method implements the scenario of “Dave Langer is no longer an Architect on any project at Microsoft”.
Law of Demeter & Collections
The above snippets illustrate the OO goodness that results from applying “Tell, Don’t Ask” to minimize coupling. However, the practical reality is that sometimes it is really darn handy to get a collection directly from an object in the domain. The reasons for this are many, but the single most common reason is to grab the collection so that it can be iterated over and displayed in a UI. Now, if you’re addicted to design patterns like I am then you’ll immediately jump to patterns like Visitor or Model-View-Presenter (MVP) as potential solutions to this problem that don’t violate the Law of Demeter.
The problem with using Visitor or MVP is that the more they are used to maximize cohesion and reduce coupling, the more complicated the class model becomes. Luckily, the Demeter folks consider containers (which they call “Repetition” classes) as exempt from the Law of Demeter. For those that are interested check out this wiki.
In terms of the Party class this means that we can expose the Party class’ collection of Role object:
1: public IList<Role> PartyRoles ()
2: {
3: return Roles.AsReadOnly ();
4: }
As the above snippet illustrates, direct exposure of the List would be inappropriate due to the fact that clients could directly add and remove Role objects directly, thereby bypassing the AssumeRole(), StopPlayingRole(), and StopPlayingRoles() methods. Additionally, the code above follows the spirit of the Law of Demeter by using the IList interface to further insulate clients from changes in the Party class implementation.
Next Time
More Party implementation goodness.
Stay tuned!
Comments
Anonymous
March 12, 2010
Is this prototype implementation of Oslo’s System.Identity? If, as I expected,must has something class or function implementtation in System.Identity.dll, let user easy to use the identity.but this time System.Identity.dll has nothing. be it under development? and when to release? thanks!Anonymous
March 15, 2010
@linze This series was very much an informal prototype of working with System.Identity from the perspective of Object-Oriented Analysis/Design. It was purely an IT guy's intellectual exercise :-). The Microsoft product team actually responsible for System.Identity has a CTP that you can look at in case you are interested in playing with a more complete API prototype. Here's the link: http://connect.microsoft.com/SystemIdentity Thanx for reading! DaveAnonymous
March 26, 2010
The comment has been removedAnonymous
March 26, 2010
@Linze Excellent, I believe I've been working with Kraig on your post to the forum. In case you're interested Kraig's published a small sample data example for System.Identity at: http://code.msdn.microsoft.com/SQLModCTPIdentity Enjoy! Dave