Reusable domain models

XAF Team Blog
28 August 2006

Reusable software components let you focus on your task. At any given moment you need to accomplish something that is important for the customer. At this point you need a basic solution for all not-so-important tasks. These solutions can be improved later or left as is if they are good enough.

When working with domain models there is the same problem. You don't want to start from an empty page. Every real business application works in some context that is assumed to be known - there are people, organizations, products, sales, contracts, tasks, schedules etc. When you start working on the domain model you are unlikely to want to focus on these entities - you would better spend your time on your core tasks. So some library of basic domain classes would help you start your work.

XAF allows you to extend such a library with UI pieces, so you will have not just a library of domain objects, but a complete application built around which you will be able to extend and customize.

So our team have this task on hand - to provide a library of domain classes and related XAF modules for you to reuse. How such a library could look like?

First there will be sub-domains of classes that incapsulate a particular part of functionality. I will call them Modules, for instance, if you want to work with tasks, you would add the Task module to your domain model. But Task is not an isolated thing. Tasks are assigned to people or groups of people, so the task module references another module (let's call it Party).

public class Task : XPObject {
  ...
  [Association("Party-Task")]u
  public Party AssignedTo {
    get { return assignedTo; }
    set { assignedTo = value; }
  }    
}

While the Task module references the Party module - it really does not care much about how Party is implemented. In fact, there can be several Party modules for different organization structures. Our developers can just have a plane list of users to assign the tasks to.

At the same time the Party class should not be aware about Task, because a particular application can not use Task at all. But after adding the Task module it will be nice to have someting like the Party.AssignedTasks collection.

public class Party : XPObject {
  ...
  [Association("Party-Task")]
  public XPCollection AssignedTasks {
    get { return GetCollection("AssignedTasks"); }
  }
  ...
}

It is not a problem to create a library that contains both modules. If you don't need the Task part, just don't use those properties and classes. But there is still a problem with module variations. If you have 2 variants of Party implementation and 2 variants of Task - you will end up with 4 libraries. And for a real library this would be thousands of combinations.

I see several possible solutions:

1. Use an untyped reference.

If we don't know the referenced type - we can make it untyped - something like this:

public class Task : XPObject {
  XPWeakReference assignedTo
  ...
  public object AssignedTo {
    get { return assignedTo.Target; }
    set { assignedTo = new XPWeakReference((IXPSimpleObject)value); }
  }    
  ...
}

In addition UI should be somehow configured to show only allowed candidates for the assignment.

To show tasks assigned to the given party - we should use filtering criteria, since there is no association defined, the criteria will sound like "find all tasks whose assignedTo refers to this Party instance".

2. Dynamically define the associations.

XPO allows to dynamically extend its metadata, so instead of hard-coding the association between Task and Party, it will be created dynamically when registering modules. XAF will be able to show these members as normal members, but you will have no typed access to them - only using the GetMemberValue and SetMemberValue methods.

3. Use persistent interfaces.

This is a natural solution for non-persistent objects. So a persistent solution can look like this:

public class Task : XPObject {
  ITaskTarget assignedTo;
  ...
  [Association("Task-Target")]
  public ITaskTarget AssignedTo {
    get { return assignedTo; }
    set { assignedTo = value; }
  }
}

[Persistent]
public interface ITaskTarget {
  [Association("Task-Target")]
  XPCollection AssignedTasks { get; }
}

Since Party does not know anything about Task, I have to create a new party that is aware about it:

public class MyParty : Party, ITaskTarget {
  ...
  public XPCollection AssignedTasks {
    get { return GetCollection("AssignedTasks"); }
  }
}

Note that currently XPO does not implement persistent interfaces and I don't know if this can be done at all. The main problem I see is that in the given app several classes can implement ITaskTarget and therefore XPO will need a way to perform queries for interfaces.

4. Create several reference implementations and give away sources that developers can combine.

This approach could seem simple, but at the same time I have no idea how possibly we can support it. It won't be a well-tested component that is plugged to your system and just works. Instead of having less code to maintain, you will have more code.

These are approaches that I currently consider. At this moment we did not choose one, so your feedback will help us a lot. Please leave your comment if you prefer one of the approaches I listed or if you have something different on your mind.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
Tags
No Comments

Please login or register to post comments.