Okay, fine – hands up – I knew that what I was doing with my entities would stir up some lively debate – thankfully the vast majority of which has not been centered around whether or not entities should have behaviour, but over whether or not the solution provided is optimal given the aims of the project. If you’re not that familiar with RavenDB then some of this discussion might not be that easy to understand - although largely it’s just a lateral step from the old debate with ORMs like NHibernate.
An introduction
I’m a big fan of the concept and intent of CQRS, and firmly believe that separating the responsibility of querying (views) from the responsibility of enacting change (commands/entities) is a good thing. This topic has been done to death, and if you haven’t heard of this concept at all, I advise you go and google “CQRS” and read what Greg Young and his cohort have to say about it.
I’m also a big fan of DDD, although I feel that it is not generally suited for incredibly simple projects like RavenGallery, I do maintain some of the programmatic consequences of utilising DDD are valuable, such as encapsulating as much behaviour as possible in my entities.
We will approach this discussion with the general acceptance that
None of this is really under discussion, but the nature of the solution implemented definitely is, now let’s look at the options we have when treating RavenDB documents as aggregate roots in this scenario.
Let’s also step away from RavenGallery for a moment, and assume we are building a much larger, more complex system with a lot of business logic and a large team of developers with the typical range of ability you tend to find in typical software departments. As stated before, it is my intent for RavenGallery to not just be “another project that cuts corners in order to demo one single piece of technology”.
Use the documents as entities and let all state be exposed via public getters/setters
As I understand it, this is @ayende’s stance, and it was my initial direction too - I would have most likely gone for if I wasn’t doing this “as if I was working in a production environment”.
This system creates a few truths
Technically, all this system needs is a well disciplined team who have been educated not to directly meddle with entities, but instead to always encapsulate behaviour in the entity.
The state that is being exposed on the entity that is not technically a part of that entity should not really ever be touched by domain logic if we work in this way.
Use the documents as entities but hide all state behind public getters/private setters
This is just a variation of the above, the only difference is that we prevent people from changing state from outside of the entity, I prefer this but it does mean creating test data is a bit tricky when testing the view layer (because we can’t construct the document whichever way we like)
You win some, you lose some.
Use the documents as entities, but hide all state in private fields with no public properties
This is nice and pure, we can use a serializer that serializes private state, not expose any state directly
Unfortunately by closing down the document/entity in this manner, we actually make it almost impossible to query the document store and get meaningful data back, unless we set all the fields to ‘stored’ and use projections for every single view.
Very pure, but on the whole quite hard to work with.
Wrap the documents in the entities, expose behaviour on entities, allow view layer to go directly to the documents
This is the controversial approach I went for, this gives us
*We are assuming here that Views in RavenDB are analogous to direct SQL access in NHibernate for generating views, theoretically they *could* modify state, but there is no reason for them to so it’s not likely to happen. The important point here is that the ‘write’ layer has no direct access to the data.
The major downside to this is that we start seeing a bit of seemingly superfluous ceremony in our repository when we come to create entities wrapped around the documents. The major up-shot is that it’s very pure, and still gives us a lot of options in the future for changing strategies with regards to how we store the data or retrieve views.
Okay then, so which one do we go for?
My personal opinion is the first one for simple projects that you know are going to remain simple, and the last one for projects that you know are going to get large and complex.
We make trade-offs by using a single data store for both storing the domain state, and for retrieving views of that data. It’s quite a good trade-off because we get really fast reads and really fast writes for free because RavenDB gives that to us, but it’s an awkward trade off because you can end up with a structure that doesn’t quite mesh with the behaviour that you want in your domain.
Summary
The full discussion can be found here, in the Google Groups, many thanks so far to those who have participated so far, I’ve had a number of people come out in favour of the wrapping approach, and a number of people come out in favour of exposing all state and just getting on with things.
The question at the end of the day seems to revolve around quite how anally retentive you can be over a simple design decision, I’m anal and I don’t mind a bit of ceremony, so sue me – I’m not going to say you’re wrong for opting for the more free and open approach – I only wish I was brave enough to.
PS
I’ll be reducing the ceremony once I know exactly how I use my objects, there are a number of options available to me, I’m just leaving it until I have a better grasp of what I need.
2020 © Rob Ashton. ALL Rights Reserved.