XCRM: Writing real-world CRM application

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.

I'm starting to write a generic CRM application, to try the new domain component (DC) technology and decide what classes should be included in our Domain Component technology. This application should be suitable for small business and extendable to support specific markets. While writing, I'm sure to find bottlenecks in XAF that we will have to fix.

In general, a CRM application should be able to manage the following things:

  • Contacts
  • Accounts
  • Leads
  • Opportunities
  • Products
  • Appointments/Calendar/Tasks
  • Service Requests
  • Marketing Campaigns

In addition, even a basic CRM system should have a security system that is aware of organization structure.

To emphasize the most interesting design parts, I won't detail every domain class. I will create most classes in brief form, and then extend them as required. In the first iteration, I will focus on class relationships.

Contact. Can belong to an Account. So, it has the Account property.

Account. Can belong to another Account. So, it has the ParentAccount and Subaccounts properties. In addition, it should have a Contacts list, and a PrimaryContact, which may not belong to the Contacts list.

I’m using TDD, so I will capture this knowledge in unit tests.

Unit tests for Domain Components

To write tests for domain components, I think I will need a DC infrastructure to be able to generate components from interfaces and business logic. I won't use the entire XAF – I will need only small parts of its services. Here is an example of a test:

[DomainComponent]
public interface IBasicTestThing {
    string Name { get; set; }
}
[TestFixture]
public class BasicTests : BaseTest {
    [Test]
    public void TestBasicThing() {
        RegisterDC<IBasicTestThing>();
        Generate();
        IBasicTestThing thing1 = ObjectSpace.CreateObject<IBasicTestThing>();
        thing1.Name = "abc";
        ObjectSpace.CommitChanges();
        IBasicTestThing thing2 = ObjectSpace.FindObject<IBasicTestThing>(null);
        Assert.AreEqual("abc", thing2.Name);
    }
}

In all my tests, I'll register and generate domain components, and use an ObjectSpace object. So, all my tests will be inherited from the BaseTest class that will provide the required services. Look how it's implemented now:

public class BaseTest {
    private IObjectSpace objectSpace;
    private IObjectSpaceProvider osProvider;

    [SetUp]
    public virtual void SetUp() {
        XafTypesInfo.Reset();
        osProvider = new ObjectSpaceProvider(new MemoryDataStoreProvider());
        objectSpace = osProvider.CreateObjectSpace();
    }
    public void RegisterDC<T>() {
        if (typeof(T).IsInterface) {
            XafTypesInfo.Instance.AddEntityToGenerate(typeof(T).Name, typeof(T));
        } else {
            XafTypesInfo.Instance.RegisterEntity(typeof(T));
        }
    }
    public IObjectSpace ObjectSpace {
        get { return objectSpace; }
    }
    public void Generate() {
        XafTypesInfo.Instance.GenerateEntities();
    }
}

The SetUp method cleans up the current type information, and creates an Object Space using a newly created Object Space Provider (using in-memory data store). The RegisterDC and Generate methods work with the XafTypesInfo system, like it's performed in XAF.

Capturing domain knowledge in unit tests

I’m ready to write a test for the Contact - Account relationship:

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

    dx.PrimaryContact = roman;
    Assert.IsNull(roman.Account);

    dx.Contacts.Add(roman);
    Assert.AreEqual(dx, roman.Account);

    IAccount xafTeam = ObjectSpace.CreateObject<IAccount>();
    xafTeam.ParentAccount = dx;
    Assert.IsTrue(Enumerator.Exists<IAccount>(dx.Subaccounts, xafTeam));
}

Enumerator is a helper class from the DevExpress.ExpressApp.Utils.dll assembly. It provides utility methods for the IEnumerable interface (similar extension methods already exist in .net 3.0).

Here is the code that makes this test pass (remember – I’m focused on relationships, so don’t tell me that the IContact should contain the IPerson interface) :


[DomainComponent]
public interface IAccount {
    string Name { get; set; }

    IAccount ParentAccount { get; set; }
    IList<IAccount> Subaccounts { get; }

    IContact PrimaryContact { get; set; }
    IList<IContact> Contacts { get; }
}
[DomainComponent]
public interface IContact {
    IAccount Account { get; set; }
}

No logic is required, because the XpoBuilder automatically generates the correct associations and XPO manages them. So, when I add roman to the dx.Contacts list, the roman.Account property is initialized automatically.

Tags
8 comment(s)
Luc DEBRUN

What !? huh !? I read the article several times and dont understand much.

Please don't tell me that you need C# MVP to use XAF in the future. If that is the direction you are going, then you will use quite a few of your clients.

Thanks.

Luc

1 October, 2008
Prolink Tecnologia

Hi,

Roman has created UnitTests before creating the application.  

He only tested the relationships.

[]s, Daniel

1 October, 2008
Roman Eremin (DevExpress)

Luc, I'm just writing what we are doing.

When XCRM will be ready, hopefully my posts will cover the whole cycle of creating real world appication "our way" (TDD, DDD, functional testing). I don't require my clients to work the same way, though I can assure that those who work our way will find XAF very easy to use.

1 October, 2008
Luc DEBRUN

Roman,

ok ... will keep reading and fingers crossed just after I down these two aspirins.

Please remember why some of us use XAF and please remember that some of us are not programmers.

Luc

1 October, 2008
Jascha

Roman - Looking very elegant so far. Just waiting for the devil in the detail ;)

Luc - keep the faith. My guess is that this will make life easier for non-programmers. If you think of each interface above as a building block, then you can compose the data records that you want XAF to provide a UI for from those blocks. So, from your perspective, the fact that XAF will come with a set of pre-built blocks representing common entities (people, accounts, products, sales orders etc.) should give you a head start.

1 October, 2008
Luc DEBRUN

Roman, Jascha,

Thanks for the encouragements.

My problem is that the objects already there in the Base.Impl are not fitting our needs. We find it easier to make our own objects. So am really wondering why all this work for yet another set of objects/building blocks. How can I be convinced that they will fit my needs better !?

What really bugs me in XAF is that no matter how I combine my objects I never really get the flexibility I want with regards to certain users being able to edit only certain properties. For example say UserRoleA can edit projects and tasks (1 project many tasks), if I want UserRoleB to only edit tasks, it has to be in a different detail view with only the tasks (if I do it in the project detail view, the tasks will be read only for USerRoleB). Somehow when reading the articles, I am dreaming that interface will make this work better... I want property based security!

Finally, am repeating here my many other posts, while all this work looks interesting to many of you, what I really want is the basics done! We still dont have reminders or recurrence in the scheduler module. No Gantt chart module like the Analysis module. No spell checker module (drag and drop as opposed to lines and lines of code). lines of code to get the pivot as a listview. There are other examples.

What I am trying to say, is that I would prefer DevEx to develop things which require LESS code in XAF.

So obviously while I appreciate Roman's efforts and the encouragements, the series of posts started badly when he said that he wanted a 'code centric way to ... '

I still keep the faith in XAF .. If Roman and Jasha say it will be easy ... That ought to be true.

Luc

1 October, 2008
Virtual Helpers Will Make Life Easier

Pingback from  Virtual Helpers Will Make Life Easier

3 February, 2010
Mike_Grace

any news on this?

11 March, 2010

Please login or register to post comments.