Blogs

eXpress App Framework Team

XCRM: Activities

This post may be outdated. For the latest Domain Components concepts and examples refer to the current online documentation.

This post is one in a series about our work on Domain Components (DC) framework and related component libraries. I’m just describing what we are working on, what problems we are facing and what results we've got so far.

This post is entirely devoted to Activities. It's more interesting to design Activities, because they are more complex than other components that I described earlier. There are three points I consider when designing Activities:

  1. There can be many Activity types: a phone call, email, meeting/appointment, task, campaign activity or case/service activity.
  2. An Activity has an owner - a person who is assigned to perform the Activity. The person should be able to see all his/her activities in one list.
  3. An Activity can be related to a Contact, Account, Lead, Opportunity, MarketingCampaign or any other “main” entity record in the system.

Let's go through all these points:

1. We can divide all Activities into two basic categories:

  • Event

An Activity that has clear start and end points. Generally, Events can be scheduled on a calendar. So, the system should be able to show all Events in one Calendar view.

  • Task

A less time-bound Activity than the Event. It may have a DueDate only.

Simple CRM systems work with Tasks and Events simultaneously. More complex systems distinguish these Activity types, so they can track specialized information, like a phone number for a call.

In our application, let’s define a Task and Event. In addition, let's create specialized Activities: a Phone Call, as a variation of the Task Activity, and an Appointment, as a variation of the Event Activity. Here are the tests that express these requirements:

[Test]
public void ActivityTypesTest() {
    ICall call = ObjectSpace.CreateObject<ICall>();
    IAppointment appointment = ObjectSpace.CreateObject<IAppointment>();
    ITask task = ObjectSpace.CreateObject<ITask>();
    IEvent eventObj = ObjectSpace.CreateObject<IEvent>();

    IList<IActivity> allActivities = ObjectSpace.GetObjects<IActivity>(null);
    Assert.AreEqual(4, allActivities.Count);
    IList<IEvent> allEvents = ObjectSpace.GetObjects<IEvent>(null);
    Assert.AreEqual(2, allEvents.Count);
}
[DomainComponent]
public interface IActivity {
    string Subject { get; set; }
    
    IActivityTarget RelatedTo { get; set; }
}
[DomainComponent]
public interface IEvent : IActivity {
    DateTime StartTime { get; set; }
    DateTime EndTime { get; set; }
    bool AllDayEvent { get; set; }
}
public enum TaskPriority { Low, Medium, High }

[DomainComponent]
public interface ITask : IActivity {
    TaskPriority Priority { get; set; }
    DateTime DueDate { get; set; }
}
[DomainComponent]
public interface ICall : ITask {
}
[DomainComponent]
public interface IAppointment : IEvent {
}

I don’t call the RegisterDC method in this test, because I've moved all Activity registrations to the SetUp method (it is called before a test is started by NUnit):

[SetUp]
public override void SetUp() {
    base.SetUp();
    RegisterDC<ILead>();
    RegisterDC<ICampaign>();
    RegisterDC<IContact>();
    RegisterDC<IAccount>();
    RegisterDC<IOpportunity>();
    RegisterDC<IEvent>();
    RegisterDC<IAppointment>();
    RegisterDC<ITask>();
    RegisterDC<ICall>();
    Generate();
}

Note that I don't register the IActivity. This is a base abstract interface that cannot be instantiated in our system.

2. An Activity has an owner – usually the system's user. The owner may have a list of Activities:

[DomainComponent]
public interface ITestUser : IActivityOwner {
}
[TestFixture]
public class ActivityTests : BaseTest {
    …
    [Test]
    public void ActivityOwnershipTest() {
        ITask blogAboutDC = ObjectSpace.CreateObject<ITask>();
        ITestUser romanEremin = ObjectSpace.CreateObject<ITestUser>();
        blogAboutDC.Owner = romanEremin;
        Assert.AreEqual(blogAboutDC, romanEremin.Activities[0]);
    }
}
[DomainComponent]
public interface IActivityOwner {
    IList<IActivity> Activities { get; }
}
[DomainComponent]
public interface IActivity {
    
    IActivityOwner Owner { get; set; }
}

3. An Activity can be related to a Contact, Account and any other “main” record in the system. Some systems allow you to link Activities to several records of different type, but I guess this is just a workaround to make programming easier. Our DC framework should be flexible, so I will design it in “the right way”:

[Test]
public void ActivityRelationTest() {
    IAccount account = ObjectSpace.CreateObject<IAccount>();
    IContact contact = ObjectSpace.CreateObject<IContact>();
    ILead lead = ObjectSpace.CreateObject<ILead>();
    IOpportunity opportunity = ObjectSpace.CreateObject<IOpportunity>();
    ITask task = ObjectSpace.CreateObject<ITask>();

    task.RelatedTo = account as IActivityTarget;
    Assert.IsNotNull(account.Activities);
    Assert.AreEqual(task, account.Activities[0]);
    task.RelatedTo = contact as IActivityTarget;
    Assert.AreEqual(task, contact.Activities[0]);
    task.RelatedTo = lead as IActivityTarget;
    Assert.AreEqual(task, lead.Activities[0]);
    task.RelatedTo = opportunity as IActivityTarget;
    Assert.AreEqual(task, opportunity.Activities[0]);
}

To make this test pass, I should define the IActivityTarget interface. Only the components that implement this interface will be able to be added to the “related to” list:

[DomainComponent]
public interface IActivityTarget {
    IList<IActivity> Activities { get; }
}

[DomainComponent]
public interface IActivity {
    
    IActivityTarget RelatedTo { get; set; }
}

Note that I have not declared a requirement that I want to be able to see all Activity Targets in one list. In that instance, we would have to create a common table for them somehow, and this might be a major restriction on possible use cases of Activities.

I don’t require all IActivityTarget implementers to be in one list, but I still need to be able to edit the RelatedTo field in a UI. This editor should let end-users locate an object of one of predefined types. CRM systems tend to achieve this by adding a combo box with object types. In this instance, end-users first select the type, and then the object of this type:

dc5-01

As far as I know,  we don’t have an editor of this kind yet. So, I've written down a task to implement this editor and added a test that describes this scenario. To tell you the truth, the XpoBuilder has trouble with this test. Let me explain in more detail.

How to persist a reference to the interface that is implemented in several classes.

Here, we have a case of leaky abstraction, impedance mismatch and that sort of thing. The fact is that a relational database is not mapped to the OO world easily. In our case – how would you manually implement the scenario where an Activity can be related to one of several records, say, Account or Contact? I see two ways (note that we exclude the requirement that the Contact and Account must have the same base).

The first approach - Multiple associations.

Make two associations:

dc5-02

The implementation of the IActivity.RelatedTo property will look like this:

public IActivityTarget RelatedTo {
    get {
        if (Contact != null) return Contact;
        if (Account != null) return Account;
        return null;
    }
    set {
        Contact = null;
        if (value is Contact) {
            Contact = value as Contact;
            Account = null;
            return;
        }
        if (value is Account) {
            Contact = null;
            Account = value as Account;
            return;
        }
    }
}

This code is ugly, but other than that it looks fine – the Contact and Account have a list of Activities. An Activity allows you to access a Contact or Account as IActivityTarget. But let’s make a simple change. Let's add the Active property to the IActivityTarget interface:

public interface IActivityTarget {
    IList<IActivity> Activities { get; }
    bool Active { get; set; }
}

XPO will not be able to apply a criteria “[RelatedTo.Active] = true” to Activities on the server, because the RelatedTo is not an FK field and cannot be used in a simple SQL query.

The second approach - Intermediate table.

Create an intermediate table for the IActivityTarget interface:

 dc5-03

In this instance, the implementation of the IActivityTarget interface in the Account and Contact would redirect all calls to the aggregated ActivityTarget class:

public IList<IActivity> Activities {
    get { return ActivityTarget.Activities; }
}

Adding extra properties to the IActivityTarget interface won’t be a problem:

public interface IActivityTarget {
    IList<IActivity> Activities { get; }
    bool Active { get; set; }
}

... because these properties will be contained in the ActivityTarget class, and the Contact and Account will redirect calls to it:

public bool Active {
    get { return ActivityTarget.Active; }
    set { ActivityTarget.Active = value; }
}

But this approach yields another problem – since we hide an indirection, any Activity-related criteria for, say, a Contact (“Activities.Count > 0”) will not be evaluated on the server, because there is no association between the Contact and Activity. As an alternative, we could translate “Activites.Count” to “ActivityTarget.Activities.Count”. This translation might not be very obvious and would lead to problems.

As you can see, that is something for XPO developers to think about. I’m going to talk to them and consider the approach we should use...

Good news! XPO can use persistent aliases to map the Account’s Activities to its ActivityTarget.Actvities. So, we will use the Intermediate Table approach with aliases, and we might not have problems related to criteria. My Activity components can work as is and I can proceed further while waiting for the XpoBuilder to generate what I want.

Published Oct 09 2008, 09:46 AM by
Filed under: , ,
Bookmark and Share

Comments

Robert Fuchs

No doubt, I like what I see about the DC framework.

But OTOH, I would rather like to see the XAF team working on those several hundred(!!!) TBDs in SC.

SCNR

October 9, 2008 10:36 AM

Mohsen Benkhellat

+1 for "The second approach - Intermediate table."

Mohsen

October 9, 2008 11:25 AM

Jascha

Hi Roman,

Still looking good - elegant even :) How do you see DC interfaces handling XPO "events" such as Changed, Saving etc.(where currently we might override OnChanged, OnSaving...)?

Jascha

October 9, 2008 11:39 AM

Roman Eremin (DevExpress)

Robert:

At the moment only one developer working with me on DC, so don't worry.

Jascha:

We will provide a way to hook DomainLogic's methods (i showed this in CaseLogic class in my previous post community.devexpress.com/.../xcrm-marketing-campaigns-and-customer-support.aspx).

October 10, 2008 2:35 AM

Alex Boston

Hi Roman,

Any news

Monzer

June 17, 2009 11:18 AM

Roman Eremin (DevExpress)

Monzer:

XPO team realized that it is not possible to make DC just on top of XPO, so there is a big rewriting going on there to support interfaces (they called it Persistent Interfaces - PI).

While we waiting for PI, we are working to make sure that other parts of XAF ready for DC.

One thing I can say at the moment is that DC and PI won't make 2009.2 release, sorry.

June 18, 2009 4:20 AM

Andy Jennings

Has anyone got the above example coded in vb they could share?

January 24, 2011 11:08 AM
LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, Silverlight, ASP.NET, WinForms, HTML5 or Windows 8, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2014 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners