Writing Domain Components framework (DC)

XAF Team Blog
25 September 2008

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

I'm starting a series of posts on my blog to share our experience while designing the domain component (DC) technology. It will be useful for us to know whether you like what we are doing. If not, we'll be able to correct our strategies early, before we make any mistakes.

I spent last month with the XAF Core team designing the subsystem that manages type information in XAF. Until now, the framework obtained this information directly from XPO metadata, sometimes using Reflection.

The new DC technology operates with interfaces. So now, the framework needs type information about interfaces. To get this information, we can't use XPO metadata, because it does not contain it. Even if we could, we don't like this approach, because we've targeted it to abstract away from a particular ORM tool. So, we decided to write a subsystem that would allow its users to avoid dealing with the sources of the type information.

When considering which approach to choose in implementing this system, Aleksey and Mike, developers from the XAF Core team, suggested an agile approach: we should refactor until the new subsystem emerges. I voted for the "Big Refactoring" approach: just write a new system, and then call it in the old methods and helpers. My approach would be faster, but it was going to lead to the crash of the entire system in a couple of weeks. This led me to do research, and partially write the type info subsystem in my private branch of XAF sources.

The next step was to integrate the new subsystem. Luckily, it was just after we had released the XAF 2008 vol. 2, the period when most of the XAF developers were busy fixing bugs and answering questions, and Aleksey was on vacation. This allowed me to convince Mike. We decided to integrate the new system and see how it behaved. Aleksey expected something like this from me. He even called us to ask if he should take one more week, so he wouldn’t see what we were doing. The next two weeks we were getting XAF back to the working state. Since then, we've been dealing with binding, type descriptors, complex property paths, structures, XPO dictionary (it directly affects what tables are created in the database), unit tests (should we reload type info between tests or not, you know, there are 1400+ tests), performance optimization and that sort of thing.

Anyway, it now seems that we have the type information subsystem working. This means that we can start creating a reference application using the DC base library. We will write this library as we go.

The base of the DC approach is the runtime interface implementation. In short, we write an interface for a domain object and a "domain logic" class that contains "non-obvious" parts of the domain object's implementation. This approach provides us with the following advantages: we avoid writing obvious code, remove dependencies on a particular ORM system, inject the aspects like security or remoting flexibly, and use multiple inheritance to construct the final application from blocks.

Real objects are generated from interfaces by the XpoBuilder class, written by the XPO team. This class is located in the XAF type information system. When you register an interface as an entity, the XpoBuilder implements an object for you. There is an important note here: you can build domain objects only once. After an object is generated, it cannot be modified.

At this point, I have the XpoBuilder working at some level, and need to have a few things implemented in the type information system. So, let's try to build an application using the DC approach.

Disclaimer: The code you see here cannot be run using XAF 2008 vol.2, or earlier. Moreover, I cannot be sure it will work in the final version of DC. This is just the code that works at the moment I wrote this post.

Let’s add a Person entity to our application. In the new DC approach, the simplistic Person library element may look like this:

	public enum Gender { Unknown, Male, Female }

	public interface IPerson {
		string FirstName { get; set; }
		string MiddleName { get; set; }
		string LastName { get; set; }
		string FullName { get; }
		DateTime Birthday { get; set; }
		Gender Gender { get; set; }
	}
	[DomainLogic(typeof(IPerson))]
	public class PersonLogic {
		public static string Get_FullName(IPerson self) {
			return self.FirstName + " " + self.LastName;
		}
	}

Now, I need to ask XAF to generate an entity for it. For this purpose, I override the Setup method in my Module class in the following manner:

		public override void Setup(XafApplication application) {
			XafTypesInfo.Instance.AddEntityToGenerate("Person", typeof(IPerson));
			XafTypesInfo.Instance.GenerateEntities();
			base.Setup(application);
		}

I call the GenerateEntities method here, because at this time XAF does not performs this call inside (I've made a note to correct this).

I run the Model Editor. As you can see, the IPerson entity is added to the BOModel node:

IPerson node in BOModel

Expanding the Views node I can see that the List and Detail Views are already generated. Oops, I cannot see a child node representing the IPerson in the NavigationItems node. I've made a note to fix this. For now, I'll add this child node manually, together with the corresponding node in the CreatableItems node:

Adding navigation and creatable items nodes

I run the application. In spite of the child node that I've added to the NavigationItems node, there is no "Person" in the navbar:

 

DC-04

After some investigation it turned out that I have no permissions for the newly generated entity. The fact is that the SecuritySimple strategy assumes that we are working with objects only. Look how it gives permissions:

	public class SecuritySimple : SecurityBase  {
		...
		protected override PermissionSet ReloadPermissions() {
			...
				result.AddPermission(new ObjectAccessPermission(typeof(object), ObjectAccess.AllAccess));
			...
		}
		...
	}

Since an interface is not a descendant of the System.Object, I have no rights to it. I've written a unit test for XAF developers. It shows that their assumption is wrong. In order to proceed, I'll write my own SecuritySystem strategy:

	public class MySecurity : SecuritySimple {
		protected override System.Security.PermissionSet ReloadPermissions() {
			System.Security.PermissionSet result = base.ReloadPermissions();
			foreach(ITypeInfo entity in XafTypesInfo.Instance.RegisteredEntities) {
				result.AddPermission(new ObjectAccessPermission(entity.Type, ObjectAccess.AllAccess));
			}
			return result;
		}
	}

My Security gives all permissions to all registered entities (all persistent types). This is not a system you would like to have in a real application, but it will let me move on.

Now, I run my application, and "voila":

DC-05

In the next post, I'll tell you about my efforts to associate objects using the One-to-Many relationship. Now, you are free to share your ideas on this post.

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.