Saturday, March 31, 2007

One issue that seems to come up regularly in the team I'm on is how to design associations between persistence ignorant domain classes, or more particularly how to do it without falling into bad design practices.

Let me provide an example of the sort of problems that come up. Let’s say we have an abstract Company class which is the base class for two concrete classes:

Agency - Contains a collection of Clients.

AgencyNetwork - A collection of agencies that work together.

A Client is associated with one Agencyand an Agency has 1+ Clients. An AgencyNetwork is associated with 1+ Agency objects, with each Agency in 0 or 1 AgencyNetworks.

The original solution is much like this (I've simplified it to show in a class diagram):

Note here that Client is coupled to Agency and Agency is coupled to AgencyNetwork with the reference being null if the Agency is not in a AgencyNetwork. The references are not bidirectional but they may need to be.

The reason the associations are like this is that we're managing the relationship from the "one end" as its simpler. Now here is what I don't like about this design:

AgencyNetworkis currently only used by one of our application so having our key Client class indirectly coupled to it seems like a very bad idea. I'd prefer our core classes, which the majority of our applications will use, to be coupled to as few non-core classes as possible.

I'd prefer if our design made it clear what the real world associations were, I don't think this design does.

I don't like the idea that the way you find out if an Client is in an Agency is like this: if(Client.Agency != null).

As I see it in a lot of the time you'll want to use an Client without knowing about a Agency/AgencyNetwork but if your working with a Agency it's likely that the first thing you'll want to do is manage the collection of Clients.

This is my alternative soluton:

Although I think its much better this solution it is not perfect either (things never are).

In object oriented terms I prefer it as Client isn't coupled to the other two. I also think that when you get an AgencyNetwork the first thing you'll want to do is look at the list of companies in it. In addition I believe that the associations are far more natural, they reflect the real world associations far better.

The disadvantage of the second solution is that managing the relationship becomes more complex. With the first solution you could do this:

// change Clients Agencyclient.Agency = newAgency;

// if the Agency is in a AgencyNetwork take it out of itclient.Agency.AgencyNetwork = null;

Personally I think this is incredibly ugly and does not reveal the intention very clearly but it is short.

With the second solution its more complex, not least as the Agency now handles the association so you have to do this (note we’re using the repository pattern here):

Although it involves more code I think this is much clearer, however where do we put this code. We cannot put this in the domain assembly as we don't want our persistence ignorant domain layer to access repositories. We'd therefore need to do it in the domain coordination layer:

a) Solution 1 - Have the one end (which will often be one of our key/core classes) manage the relationship and so enforce things that way (resulting in a lot of coupling).b) Solution 2 - Decouple the classes, add the associations that we think make most sense and then have to explicitly manage the relationship.

If your developing an information management system then its quite likely you'll need to be represent and evaluate large numbers of business rules in your domain model. For example if an Order over £10,000 must be authorized by a Manager then we'll have to represent that somewhere in the domain.

Before looking at how to represent the rules its worth thinking about how you'll evaluate the rules. One option is to have an IsValid property on each domain object that has validation rules, however the question is then "valid for what"?

If we mean persistence then we should rename it as IsValidForPersistence. This might be useful as if an object was valid for its current state (e.g. Active) when it came in from the database then when we save it we will expect it to still be valid for that state.

However although IsValidForPersistence might be useful it doesn't give us all the functionality we'll need because in many cases the validation has to be state based. As an example we don't mind an unauthorized Order over £10,000 sitting in the New state, we only need it to be authorized by a Manager before it moves to the Active state.

We've solved this by using state based rule methods, so you could call order.GetBrokenRulesForTransitionTo(OrderState.Active) and get back a collection of BrokenRule objects with descriptions about the broken rules.

This works very nicely and has allowed our GUI to display a list of reasons that an object can't move to another state whilst at the same time allowing us, in the domain, to ensure that we can avoid state transitions that the object is not yet ready for.

Having used Team System for about a year now I've come to quite like it, bits of it could be improved but as a developer it is quite a nice environment to work in.

However there is a serious problem with its usage within an agile team, the problem being the outrageous cost of getting users involved.

For example we want to get our users involved early on in the requirements process and to do this we're planning to use a user story based approach. Personally I think it'll work far better than the requirements document or use case approaches that I've used in the past.

Anyway to make this process work we need a way to get our users involved and since they are at the other end of the country from us we have to look at combining software with regular phone calls.

With this in mind I've used the Process Editor bit of the Team Foundation Power Tool to create a custom user story work item and we're getting our users to trial TeamPlain so that they can see/modify the actual work items.

The idea is that they specify the work item and that it will then move through multiple states. It will start in a new state, once the users have specified acceptance tests they can move it to a signed off state, we in the development team will than handle moving it to the in progress and complete states and finally when the users get the release they can use the acceptance tests to test the functionality before moving it to a closed state (if all goes well).

I think this should work fine and, despite a few issues, TeamPlain likes a good tool for our users to play with.

The only problem, and its a huge one, is that in order for our users to interact with Team System in the way we want them to each of them will need a client access license. This will add massively to the cost of team system and goes against the aims of agile to a point that seems ridiculous.