In this post we’ll take a quick look at the upcoming technology preview for Domain Components, which will ship with 10.1. The “What’s New” file for this technology preview provides the following elevator pitch:
“The Domain Components Technology provides an elegant and intuitive way to create reusable modules. Now you can compose an application from reusable blocks abstracted from a particular persistence layer.
With the Domain Components Technology you basically define interfaces instead of declaring regular business classes. Then you specify the logic that defines how interface members behave. After that you can reuse and combine these interfaces to quickly create required domain components. The actual business classes are automatically generated by XAF at runtime.”
So the purpose of the DC technology, then, is to allow developers to produce reusable domain libraries that are abstracted from a particular persistence layer.
Currently, when using XAF, developers implement business classes that encapsulate domain knowledge. This has two disadvantages, firstly XAF only supports XPO as a persistence layer at this time, which means you must inherit from the XPO base classes and reference the XPO assembly. Secondly business classes cannot be combined as there is no multiple inheritance in C#. This means that developing a reusable domain library, abstracted from a persistence layer, is very challenging.
DC will remedy this by allowing a developer to define interfaces and the implementation of their members. These interfaces can then be used to compose other interfaces before, finally, you register the required interfaces with XAF, which will generate the actual business classes at runtime time.
Right, let’s have a look at a quick example. We’ll implement several domain components, let’s start with an IPerson interface:
[DomainComponent, ImageName("BO_Person")]
public interface IPerson {
[RuleRequiredField]
string LastName { get; set; }
string FirstName { get; set; }
string FullName { get; }
DateTime Birthday { get; set; }
bool Married { get; set; }
string SpouseName { get; set; }
}
There are a couple of things to note here, firstly that you can apply regular XAF attributes (RuleRequiredField, ImageName, etc) just like you’d do with a “normal” business class. Secondly, that the interface is decorated with the [DomainComponent] attribute, specifying that this interface describes a Domain Component. Of course, we could use this interface as is, but that’s not very practical, so let’s go ahead and add an implementation class:
[DomainLogic(typeof(IPerson))]
public class PersonLogic {
public static string Get_FullName(IPerson person) {
return string.Format("{0} {1}", person.FirstName, person.LastName);
}
public void AfterChange_Married(IPerson person) {
if(!person.Married) person.SpouseName = "";
}
}
The last thing I have to do, before I can use my Person object, is to register the IPerson interface with XAF, like so:
public sealed partial class MySolutionModule : ModuleBase
{
public override void Setup(XafApplication application)
{
if (!XafTypesInfo.IsInitialized)
{
XafTypesInfo.Instance.AddEntityToGenerate("Person", typeof(IPerson));
}
base.Setup(application);
}
}
Which causes XAF to generate my class for me:
Now I can go on and define the IOrganiszation interface with a collection of IPerson objects:
[DomainComponent, ImageName("BO_Organization")]
public interface IOrganization {
string Name { get; set; }
IList<IPerson> Staff { get; }
}
This simple declaration will automatically set up an association:
Here is an example of method implementation and initialization logic:
[DomainComponent, ImageName("BO_User")]
public interface IAccount {
[SizeDc(8)]
string Login { get; set; }
[SizeDc(8)]
string Password { get; set; }
string GenerateString();
}
[DomainLogic(typeof(IAccount))]
public class AccountLogic {
public static string GeneratePassword() {
byte[] randomBytes = new byte[3];
new RNGCryptoServiceProvider().GetBytes(randomBytes);
return Convert.ToBase64String(randomBytes);
}
public static void AfterConstruction(IAccount account) {
account.Login = GeneratePassword();
account.Password = GeneratePassword();
}
}
Notice the use of the “SizeDC” attribute here. This attribute is analogous to the “Size” attribute that you will be used to decorating your current business classes with. Note also that this name is likely to change before the final version of DC ships. :-)
Now that we have a number of interfaces defined, we can package them in an assembly and use it as a domain library. This means that I’d be able to create a new XAF application, reference the domain library, and define a new component this this:
[DomainComponent, NavigationItem, ImageName("BO_Contact")]
public interface ICustomer : IAccount, IOrganization {
}
public override void Setup(XafApplication application) {
if(!XafTypesInfo.IsInitialized) {
XafTypesInfo.Instance.AddEntityToGenerate("Person", typeof(IPerson));
XafTypesInfo.Instance.AddEntityToGenerate("Account", typeof(IAccount));
XafTypesInfo.Instance.AddEntityToGenerate("Organization", typeof(IOrganization));
XafTypesInfo.Instance.AddEntityToGenerate("Customer", typeof(ICustomer));
}
base.Setup(application);
}
Now I have a customer domain component which I can use:
We think the this DC technology is a real step towards simple development of reusable libraries and should make the lives of developers a lot easier, we hope you enjoy playing with the technology preview when it ships with 10.1.
Until next time, happy XAFing! :-)