Hi All,
I've been working my way through the documentation and community content around the design of applications using RavenDb. I would like to get some advice on best practices (and opinions) with regards to the design of business objects and the propagation of those objects into an application (looking more at a client/server type application than a web solution). I'm not talking about the general design of entities for a Document-base datastore (I think Ayende has covered this quite nicely in the documentation and augmented it with his discussions on Includes, Composite Objects etc), I am pondering the following types of questions :
- should you do things like implement INotifyPropertyChanged / IDataErrorInfo on domain objects?
- If not, that would imply mapping the entities to models (assuming an MVVM type approach) for data binding (possibly using something like AutoMapper)? Does this not add additional complexity to the solution (managing the mapping/sync/saving/binding processes between data entities and interface models)? How does one manage this?
- What types of Collections are best for Domain Objects? (returning to the line between interface/domain) Is it feasible to use collections that implement change notification here too?
- These questions become especially pertinent when you consider the use of technologies like Rx (Reactive Extensions) with the RavenDb Changes API (to build responsive, reactive interfaces). To my understanding, in these situations the management of push-based changes to the UI should be as simple as possible. This seems to suggest less mapping-type scenarios?
- What about validation? There seem to be many schools of thought around this (ranging from Data Annotations to separated Validation Decorator classes). Any suggestions?
- What other pertinent design considerations should I consider when architecting a solution of this nature which are specific to the concerns of implementing a Document-DB oriented application?
I realize that this line of questioning is quite broad, and I apologise if this represents an "it depends on your particular scenario"-type question, but there must be a set of generally accepted guidelines and best practices representing the best way to optimally structure the management of a Document-Db-centred business domain (using the same design principles of convention and simplicity that make RavenDb such an excellent product) ?
After reading some of Ayende's reviews of software, it seems all-too-easy to fall into the trap of badly architecting a system based on widely accepted advice which may not ~actually~ be all that accurate....
With thanks in advance,
Ryan
Hi Oren,
Thank you for the detailed response - it never ceases to amaze me how much of yourself you invest into the community. Your time is much appreciated.
If I may summarize a couple of things based on your response to wrap this up, for your commentary where necessary (taking into account that this speculation represents a starting point with context playing a big role in the details of the implementation); would this be a good approach to take for a green-field business application written in WPF as a baseline guidance (and to summarize for anyone else who may be trying to piece together the same understanding)?
- Create small DDD-oriented problem domains with Aggregate Root Entities and control access to child entities through this entity
- Fracture monolithic entities into smaller composable parts based on the roles which will use them - tie these entities together with a parent entity which contains only the bare minimum of "common information". Control access/management through the individual functional modules working with the domains and handle the management of the "parent" entity separately as an administrator function. If the fractured entity-leafs exist in separate databases then use replication on the parent entity to tie them together.
- Avoid relationships between entities where possible, but where a relationship is necessary use a DeNormalised reference to store the information required to easily display the name of the referenced entity as well as the ID to use for a look-up where required. Use the Include function in your Queries to pre-load related entities to minimize trips to the database
- Implement IDataErrorInfo, InotifyPropertyChanged etc on your domain entities and use List<> as your collection (if I may ask, what is it about ObservableCollection<> that you don't like?)
- Avoid creating mapping classes unless there is a compelling reason to do so
- Avoid using the Repository pattern and leverage the UOW inherent in the RavenDB Session directly. Do not create layers of abstraction needlessly or avoid the use of these objects directly.
- use a single session per form and avoid shared state in favour of an even broker
- model your UI closely to your domain
- can you clarify what you mean by proxy classes that can be created at runtime? Do you have an example of this that you can point me to?
- write unit tests using an in-memory version of RavenDB (is there a good example of solid unit-testing in your own repositories that you can point me to?)
Hi Oren,
Thank you for the quick response.
What I meant in item # 2 is described here : http://ayende.com/blog/153704/composite-entities
I seem to be missing you here:
public class Item
{
public string Name {get;set;}
}
public class Item_AutoGenerated_Proxy : INotifyPropertyChanged
{
public Item Inner {get;set }
public string Name {
get { return Inner.Name;}
set { Inner.Name = value; OnPropertyChanged("Name");}
}
}
Do NOT write this manually, do this on the fly.
How do I “do this on the fly”? Are you using a specific tool?
Ryan
For your reference, I found a cool snippet for doing this using DynamicObject (see below, gist : https://gist.github.com/2730528 ).
Do you think there would be any notable performance considerations using this approach over a graph of objects?