XCRM: Marketing Campaigns and Customer support

XAF Team Blog
07 October 2008

EDIT:
Domain Components (DC) is in maintenance mode and we do not recommend its use in new software projects.

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.

Marketing Campaigns

A Marketing Campaign has no relationships. However, a Lead and Opportunity may reference a Campaign:

[Test]
public void CampaignRelationships() {
    RegisterDC<ILead>();
    RegisterDC<ICampaign>();
    RegisterDC<IContact>();
    RegisterDC<IAccount>();
    RegisterDC<IOpportunity>();
    Generate();
    ICampaign campaign = ObjectSpace.CreateObject<ICampaign>();
    campaign.Name = "Playboy back cover";
    ILead lead = ObjectSpace.CreateObject<ILead>();
    IOpportunity opportunity = ObjectSpace.CreateObject<IOpportunity>();
    lead.Campaign = campaign;
    opportunity.Campaign = campaign;
}
[DomainComponent]
public interface ICampaign {
    string Name { get; set; }
}
[DomainComponent]
public interface ILead {
    
    ICampaign Campaign { get; set; }
}
[DomainComponent]
public interface IOpportunity {
    
    ICampaign Campaign { get; set; }
}

Instead of adding the Campaign reference property to both the ILead and IOpportunity components, I could add the ICampaignResult interface and inherit the ILead and IOpportunity from it. But I think, this is overkill. However, if one more class references the Campaign, and business logic is supplied for this reference, I'll choose this approach.

Cases (customer support incidents)

A Case is a record about a user's inquiry or problem. It is linked to an Account and/or a Contact. If the Contact is set, the Account is initialized from the Contact’s Account, if any:

[Test]
public void CaseRelationships() {
    RegisterDC<IContact>();
    RegisterDC<IAccount>();
    RegisterDC<ICase>();
    RegisterDC<IOpportunity>();
    Generate();
    IContact roman = ObjectSpace.CreateObject<IContact>();
    roman.FirstName = "Roman";
    roman.LastName = "Eremin";
    IAccount dx = ObjectSpace.CreateObject<IAccount>();
    dx.Name = "DevExpress";

    ICase case1 = ObjectSpace.CreateObject<ICase>();
    case1.Subject = "1";
    case1.Contact = roman;
    Assert.IsNull(case1.Account);

    ICase case2 = ObjectSpace.CreateObject<ICase>();
    case2.Subject = "2";
    case2.Account = dx;
    Assert.IsNull(case2.Contact);

    roman.Account = dx;
    ICase case3 = ObjectSpace.CreateObject<ICase>();
    case3.Subject = "3";
    case3.Contact = roman;
    Assert.AreEqual(dx, case3.Account);
}

[DomainComponent]
public interface ICase {
    string Subject { get; set; }
    IAccount Account { get; set; }
    IContact Contact { get; set; }
}

To make this test pass we need an extra code, in addition to the interface. We need domain logic for the ICase to initialize the ICase.Account property by the ICase.Contact's Account:

[DomainLogic(typeof(ICase))]
public class CaseLogic {
    public static void AfterChange_Contact(ICase self) {
        if (self.Contact != null && self.Account == null) {
            self.Account = self.Contact.Account;
        }
    }
}

Here is how we are planning to add domain logic to the classes that are generated by the XpoBuilder. We are going to use naming conventions. If the XpoBuilder finds a method named “AfterChange_XXX”, it tries to call this method in the generated setter of a XXX property. We'll probably add the attribute-based binding in the future.

A Case should have a human-readable ID that can be used in communications to quickly reference the Case. This ID should be generated only once, when a new object is beeing created. In addtion, the ID property should be read-only, but persistent.

Here is one more DC design challenge: how domain logic can specify the initial value of a read-only property? Should I add an AfterConstruction method to the business logic to set a value? But in this instance, I'll have to provide some system interfaces to call the ID’s setter. So, I think it is better to implement the Init_XXX domain logic methods that will allow you to set a property's initial value for a newly created object.

Since the ID property doesn't have a setter, it won't be persisted by default. To make this property persistent, I've decided to decorate it with the PersistentDc attribute. It could make sence to call this attribute Persistent, but in XAF this name conflicts with XPO’s Persistent attribute. So, it will be PersistentDc for now, and we will see what to do with it later.

[DomainComponent]
public interface ICase {
    [PersistentDc]
    string ID { get; }
    
}
[DomainLogic(typeof(ICase))]
public class CaseLogic {
    public static string Init_ID(ICase self) {
        //ToDo: Change to "select count(*)+1 from case" analog
        return Guid.NewGuid().ToString();
    }
    
}

This is a very simplistic implementation. I will return with a more realistic code, as soon we have a proper infrastructure.

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.