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
Mark Krasnohorsky
Mark Krasnohorsky

Hi Roman,

Excellent! So far, I like it.

Also, I really appreciate these posts as they give a good feel for what it will look like and I don't have to wait in anticipation (or dread) for the final release.

Thanks,

Mark

25 September 2008
Mark Krasnohorsky
Mark Krasnohorsky

Hi Roman,

I presume that XPO knows to persist a property if it has a both a get; and set; in the interface. However, how would one define a read/write property without making it persistent? Can the property declaration be decorated with standard XPO attributes?

Thanks,

Mark

25 September 2008
Roman Eremin (DevExpress)
Roman Eremin (DevExpress)

Mark:

Yes, we will have a mechanism how to define this. Semantially it wll be enough to provide getter and setter logic, but probably explicit attribute is better.

I will write as we progress, so probaly we will stumble on real scenario.

25 September 2008
Mohsen Benkhellat
Mohsen Benkhellat

Hi Roman,

First impression is good.

A few questions:

1-Would this support inheritance between interfaces ?

2-Would we be able to load (or any other xpo action for that matter like FindObject etc) a collection of IPerson as well as a collection  of Person depending on needs?

I think being able to modify an already built object by adding/removing interfaces would be important to be able to adapt an application adapt to a customer business model at run-time.

Exciting stuff,

Mohsen

25 September 2008
Anonymous
Alain

Hi Roman.

As I can see, the property declaration is located at interface, but what about if I want to write some code in get or set method of one specific property?

Regards, Alain

25 September 2008
Linton
Linton

"you can build domain objects only once. After an object is generated, it cannot be modified."

Can you explain that statement a bit further. I assume you mean that the object cannot be modified at runtime...but please clarify.

Linton

25 September 2008
Roman Eremin (DevExpress)
Roman Eremin (DevExpress)

Mohsen:

1 - yes, this was the whole point

2 - Yes, i don't see why not.

Alain:

Please check the first code fragment - there is an implementation of FullName getter specified in the domain logic class.

Linton:

Sorry, i wasn't clear - i meant domain classes, not objects. Domain classes are actual implementation of specified interfaces. What I meant is that you won't be able to add, say, one more association between A and B after generation.

My point was actually about the fact that it is not correct to call GenerateEntities in one module setup, because if you have several modules - you'll have to call GenerateEntities only once.

Roman

25 September 2008
Anonymous
Alain

And the setters will work in the same way?

What about InmediatePost attribute ??

And what about with computed properties? or dependent properties (change in prop A need to fire a change in prop B)

Regards

25 September 2008
Alex Hoffman
Alex Hoffman

I realise that the interface is the specification that is used to generate the model.  But given that an implementation contains only behaviour, it would be ideal to be able to specify a *particular* implementation in code or via configuration.

In other words, provide a version of AddEntityToGenerate() that also takes (as a parameter) an implementation class, and also support the specification of a particular implementation at runtime via a configuration (xml) file setting.

Mind you, I notice that the implementation class is static (?), and that the GetLastName method takes an IPerson.  Typically, DI containers will generate an instance of the dependency a dependent object requires.  Does this solution do away with instances and assume a Static implementation class?  That would be limiting.

Finally, I'm hoping that aspects of the model like the views, are not generated from the interface, but rather they are based on the model - that's hopefully already the case.

-- Alex Hoffman

expressapp.blogspot.com

25 September 2008
Luc DEBRUN
Luc DEBRUN

Please remember where XAF started from.

It was "create your XPO classes, link them if needed, decorate them with attributes if needed, run and voila it works! "

If you replace this by "create your interfaces, link them if needed, decorate them with attributes if needed, run and voila it works!" then am ok.

If now we need to add code to GenerateEntities, Add code for permissions, add code for this or that - then am no ok.

I like XAF because it gives me a lot without code.

Secondly, if in your studies you find a way to implement security at the property level rather than at the object level, then do spend some time there. I think it would be useful.

Luc

25 September 2008
Roman Eremin (DevExpress)
Roman Eremin (DevExpress)

Alain,

Of course we will keep the functionality that was previously.

Alex:

Yes, technically it is not a problem to specify DomainLogic in configuration.

Static domain logic implementation is just one variant, XpoBuilder can use non-static version too.

About views and everything - the primary goal of DC technology is to promote reusability of domain components. Which means you should be able to reuse some IMedicalInsurance with its domain logic, views, controllers, actions, rules etc. and then tailor it to your needs. So obviously information about IMedicalInsurance should be in the model.  

26 September 2008
Roman Eremin (DevExpress)
Roman Eremin (DevExpress)

Luc:

I'm just writing what we doing - to be more transparent. Of course in the final version everything will work with interfaces like it works with classes now, or even better.

26 September 2008
Anonymous
eXpress App Framework Team

In the first post of the series devoted to our new domain component (DC) technology , I tried to show

26 September 2008
ProfK
ProfK

I love what you've done with DC, but what I really don't like is the post date being hidden at the bottom of your blog posts. It means I have to scroll right through the post to see if it is current enough to be applicable to what I'm doing.

31 October 2011
Roman Eremin (DevExpress)
Roman Eremin (DevExpress)

Brady:

We will update all DC posts with links to current information.

1 November 2011

Please login or register to post comments.