So far, everything you say sounds reasonable to me. You modularize certain areas, define interfaces on different levels of abstraction and export each of your modules with different interfaces, i.e. each module will be registered as several interfaces it implements. You'll be able to resolve your modules according to the level of abstraction you require, get all services, all business areas, etc. So, in my opinion you are heading in the right direction.
How to deal with metadata? MEF provides exporting of metadata, which would appear to be a reasonable thing to use. Maybe I didn't get it entirely, but I have made really bad experiences with MEF metadata export. As far as I can recall, MEF stores metadata as key-value pairs, where the key is a string.
Even if you use the typed metadata export and usage functions, the metadata is not really typesafe. Say you have an interface 'IMetadata' with a property called 'PropertyA' and you register a type (lets call it Foo
for the sake of creativity) which is decorated with an according attribute in a, what MEF calls typesafe, manner (metadata implementing IMetadata). Now suppose you have a second metadata interface 'IMetadataB' which also has a property called PropertyA
. If you now request resolving Foo
with metadata IMetadataB
you'll get the instance you registered in the first place because MEF is satisfied with PropertyA
being existent in the metadata key-value pairs and builds an according proxy metadata type which implements IMetadataB
.
Long story short, maybe I'm being unfair to MEF, but I stopped using the MEF built in metadata support and would recommend you to do the same.
As I'm dealing with very complex and lengthy metadata, including kind of documentations of classes which I want to be tightly coupled to the classes I export, I developed a system which works really well for me, although it is a little bit unconventional:
Basically, I define an interface and a baseclass for my metadata, say MetadataBase
with a string property called Description
.
public class MetadataBase : IMetadata
{
public string Description { get; set; }
}
Afterwards, for every class I want to have metadata for, I derive a class (FooMetadata
) from this base class and define it partially in XAML. Then, in XAML, I define the class specific value of the property, for example:
<md:MetadataBase.Description>
The description of my class goes here
</md:MetadataBase.Description>
With a custom attribute, I relate the metadata type to my actual class:
[Export(typeof(IFoo))]
[AssociatedMetadata(typeof(FooMetadata))]
public class Foo : IFoo
{
// Whatever
}
An extension method for objects lets you read the metadata via reflection:
public static IMetadata GetMetadata(this object objectWithMetadata)
{
// Read attribute type
// Create instance of the metadata type, i.e. FooMetadata
// A caching mechanism can be implemented, if needed, but, honestly,
// my really big metadata objects including images and stuff like this
// are created within 3-5 ms
// Return this instance
}
Now, you're basically there, you can read the metadata of any object which has metadata like this:
var myObjectsMetadata = myObject.GetMetadata();
You can utilize this metadata in MEF when you make your AssociatedMetadataAttribute
implement an interface and register your types with metadata of this interface. Nothing will get mixed up, because you have one type of metadata for everything which holds one property and nothing else (the type).
This solution is not the right way to go for everything, but I love it and your question is a good occasion to present it. Hope it helps!