Re: [RavenDB] Design Considerations for Domain Objects

167 views
Skip to first unread message

Oren Eini (Ayende Rahien)

unread,
Jul 27, 2012, 12:15:56 PM7/27/12
to rav...@googlegroups.com
Ryan,
The major issues is that your question is TOO broad, the answer is, it depends.

I caution that I am speaking without context, and this represents solely my _default_ approach from the get go, and is subject to change based on... (you get the drift).



On Fri, Jul 27, 2012 at 6:47 PM, Ryan Britton <ryanbritto...@gmail.com> wrote:
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 :
  1. should you do things like implement INotifyPropertyChanged / IDataErrorInfo on domain objects?
Yes... although note that you should NOT share entities between more than a single form at a time.
One session per form, and no shared state between them.
Communication between them through event broker.

You can see this type of architecture here: http://github.com/ayende/effectus
There is a whole MSDN article about this app, it talks about NHibernate, but much of the same applies.


Assuming that the only thing that can change is the UI, I like to create proxy classes (can be auto generated at runtime that implement INPC, wraps collections etc) and take care of all the UI stuff for you.
This assumes that you won't make direct changes to the entities yourself that needs to be reflected back.
 
  1. 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?

The problem with doing so, which is sometimes required, is that you are duplicating a LOT, and usually not earning much.
Usually we model our entities based on our usage scenarios, which closely match the UI, so there isn't even a good reason to create View Model to compose multiple entities.

 
  1. 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?

List<T> is what I usually use.
You _can_ use ObservableCollection<T>, though, but I am not sure how much I like it.

 
  1. 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?

The Changes API only expose the key of the changed item, not the actual document itself.
But less mapping is better.

  1. What about validation? There seem to be many schools of thought around this (ranging from Data Annotations to separated Validation Decorator classes). Any suggestions?
You can do that in a Listener if you want, how you do it depends on what you want, what is easier and what side of the bed you got out of this morning.
 
  1. 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?

Fallacies of Distributed Computing
SOLID
etc


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




Ryan Britton

unread,
Jul 28, 2012, 4:52:49 AM7/28/12
to rav...@googlegroups.com
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)? 
  1. Create small DDD-oriented problem domains with Aggregate Root Entities and control access to child entities through this entity
  2. 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.
  3. 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
  4. 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?)
  5. Avoid creating mapping classes unless there is a compelling reason to do so
  6. 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.
  7. use a single session per form and avoid shared state in favour of an even broker
  8. model your UI closely to your domain
  9. 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?
  10. 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?)

Once again, your valuable input is much appreciated.
Keep up the inspirational work in this space  :)

Ryan

Oren Eini (Ayende Rahien)

unread,
Jul 28, 2012, 5:28:32 AM7/28/12
to rav...@googlegroups.com
inline

On Sat, Jul 28, 2012 at 11:52 AM, Ryan Britton <ryanbritto...@gmail.com> wrote:
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)? 
  1. Create small DDD-oriented problem domains with Aggregate Root Entities and control access to child entities through this entity

Yes 
  1. 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.

I don't understand what this means. And I am not sore what you mean by smaller composable parts.
 
  1. 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

No, you can use includes instead, denormalize only when needed.
 
  1. 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?)
It is just annoying, to be honest. You can use OC there, I just don't like it. Personal choice, not a technical one.

 
  1. Avoid creating mapping classes unless there is a compelling reason to do so
Yes 
  1. 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.
YES 
  1. use a single session per form and avoid shared state in favour of an even broker
Yes 
  1. model your UI closely to your domain
Yes 
  1. 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?

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.
 
  1. 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?)

Look at the ravendb tests.

Ryan Britton

unread,
Jul 28, 2012, 5:40:33 AM7/28/12
to rav...@googlegroups.com

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

Oren Eini (Ayende Rahien)

unread,
Jul 28, 2012, 5:51:36 AM7/28/12
to rav...@googlegroups.com
Generate this using codegen somehow, either at compile or runtime.

Ryan Britton

unread,
Jul 28, 2012, 6:19:02 AM7/28/12
to rav...@googlegroups.com

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?

Oren Eini (Ayende Rahien)

unread,
Jul 28, 2012, 6:21:22 AM7/28/12
to rav...@googlegroups.com
Not in a client app, no
Reply all
Reply to author
Forward
0 new messages