NHibernate Read Models: An Elephant in the Elevator
Our company website is a mostly well-architected NHibernate web application. There’s a few data access related performance problems, like ghost writes. I’m confident these issues account for most of the slowness, but the site is still pretty snappy considering we just set debug=”false” and hit publish.
Entities were modeled after the business and mapped on to a proper 3rd normal form database with no aggregate boundaries. The object graph for a given query can easily be 5 or 6 layers deep. Translated in to SQL, this means an explosion of joins. The vast majority of the database is populated nightly from SSIS packages and otherwise completely read-only.
I’ve seen this movie before. Hell, I’ve directed, starred in, and won an Oscar for this movie. Haven’t we all? The database is modeled after the business, and then pinched and pulled and squished to service a query-heavy application or query-only web site. By the time you’ve finally pushed that NHibernate elephant in to the elevator, you and your app are bruised and brittle.
We did all that work to get to a point where we’re murdering our SQL database with the same joins over and over. It would require MUCH less processing power to pre-calculate each view model and make it easy to fetch quickly.
This past week, I attended the NServiceBus Boot Camp. What an amazing Christmas gift! I can’t recommend it enough. I wish we’d had time to dig in to CQRS read models more, but here’s a few points on the topic:
- The read model SQL database lives on the web server. Think of it as a cache, just more explicit.
- Domain processing happens elsewhere and publishes events.
- Handlers on the web server handle those events and update the read model accordingly. NHibernate feels heavy here. Consider a Micro-ORM.
- Reads go directly to the local database. Query with something really light, then bind the result to the page.
Though I haven’t tried it, I suspect RavenDB would be a good alternative to SQL on the web server.