Wednesday, September 1, 2010

ORMs for all?

I believe we have done a discussion post on IoC and Repositories. For IoC it sounded like we were all in agreement that once you get into the pattern it's hard to not use it, the complexity fades away, and the extra upkeep saves you code in other ways. For Repositories I believe hit on trying to properly use the term and trying to restrict their abilities. Now I'm thinking we should do a post to discuss the use of Object-Relational Mappers or ORMs.

I personally am finding the use of an ORM almost always worth it. Your code takes a completely different shape and a lot of worries you had to deal with yourself almost vanish and are replaced with much easier to maintain ORM configuration code. That is once you learn the ORM.

The majority of my experience is with NHibernate and I am still learning this beast, but it has some extreme power at your figure tips. You sometimes almost feel like an evil conjurer summoning the magic of powerful dark wizards (this is natural because you are). The problem is of course that NHibernate adds it's own sort of spells and rituals to the mix that you have to learn. These spells and rituals also can blowup in your face in a different manner then what you might be used to from ADO spell casting. So there is a learning curve in order to gain easy connection/transaction managment, object change tracking, query batching, non-magic string statically typed queries, sophisticated mapping maintained in a single location, lazy loading, and the ability to have a fuller more meaningful object model.

Now this is with NHibernate which is trying to support the abilities of mapping a fuller object model that is completely independent of the database schema.  If you are learning an ORM that went along with a more ActiveRecord pattern then it's probably much easier to use. Generally the easier it is to use though the less flexible so you could run into problems down the run, but you application may never need that. Going with straight ADO will probably be easier to write and get traction when you don't know an ORM, but now you are managing everything yourself. Even if the code is generated it's still code you have to maintain. Naturally your application's DAL and database may never change so managing all that manually may never come into effect.

There is of course the loss of performance and the added overhead to using an ORM. I really don't think this is a problem in our world. ORMs can help optimize database access, in all seriousness, but then you may be biting the bullet on CPU cycles and memory, but that's what we have the most of anyway. Of course there are  system/architecture designs that take care of most bottlenecks.

You also have less transparency with an ORM over ADO and SQL. The ORM has to interpret your query and generate the SQL in order to take full advantage of its mappings. You can run into problems of debugging  problems because you didn't realize the ORM was going to doing something. This goes back to the learning curve that can be involved for a team, but I rather dislike second guessing myself when I just want to rename or change the mapping for a property.

I think I have hit the majority of concerns with using an ORM and I'll restate my opinion here with a bit more explanation.

I personally find it hard to think of a project where I wouldn't be using an ORM of some kind. This does not mean that you get to avoid learning about databases and SQL. That complexity is there and constant, but it does mean that you get to avoid writing mapping code, casting the spells around ADO (or whatever framework library you have), have to write your own change tracking for dynamic updates, lazy loading, working with dummy objects and more procedural style code, and others.

That last one about procedural code could be a whole other post, but I think that when it's difficult to create and map a fuller object model, developers will create a dummy object model, and that leads to procedural code. I think most of us understand the side effects of medium+ amounts of procedural code (hard to maintain, easy to make mistakes).

So what do you guys think about ORMs and their use? Certainly we all want to be pragmatic, but, unless the project is very small,  I find it hard to not think about using one. ORMs seem to becoming like an IoC container for me. This might make it hard for me to gel well with some teams though.

5 comments:

  1. Where I'm currently at with ORMs is that the software ecosystem of my current job is _very_ procedural. I can't really blame them, it's a very procedural business and that layout of the code is just easier to grok and to support overall in this case. Not to mention that there is no "core domain" here, just separate applications doing separate things as part of a larger scope of "procedures." (I.E. App1 picks up some files and does something and writes to a DB, App2 later reads from the DB and does something and writes to another DB, App3 later does something else based on that data, etc.)

    In putting together code designs to fit this model, I've ended up with a very procedural anemic domain model (not unlike the one that was being built at our previous job, but a bit more procedural here). What this means is that the power of an ORM becomes less and less evident in this design. Thus, I've yet to really hit on one that gives proven results in this environment.

    Take lazy loading as an example. If the data access method is for an app to hit a service, which in turn asks a DAL repository for some data, does something with that data, and returns it to the app... where would lazy loading come in? The service's methods shouldn't be ORM-aware, should they? And any hope of lazy loading is gone once the data is sent back over that service boundary.

    Granted, the client's consumption of the service is itself an object model, so the client can lazy-load by having its objects make calls to the service. But if the effort here is to make the client apps as "dumb" as possible and have all the logic behind the service boundary, then that's complicating what should ideally be a very small and simple client application.

    And from the perspective of the service's methods and logic, there are _many_ repositories. Databases (of varying make and model), file system, 3rd party APIs, external services, etc. There's no "one database" that the overall domain uses. So, again, the benefits of an ORM don't really fit this scenario.

    If anybody has any thoughts on the subject, I'd love to hear it. I'm currently debating the idea of moving that service boundary up the stack and keeping the logic in the client apps, making their own individual models heavier. Not sure what ORMs would be able to do for me there either, though. And I fear that this would lead to increased diversification of logic and design and not really fix the mess we have now. And there's really something to be said for the testability/reproducibility/debugability of being able to encapsulate the logic into a request to be made to a single service, rather than having to reproduce steps in an unfamiliar application.

    ReplyDelete
  2. Yeah I haven't really been in an environment like that. This is why I like these discussion posts.

    What you have sounds like some kind of early take on SOA. I have heard that a lot of old systems were done this way and were generally not very clean.

    I'll have to think about your scenario some, but hopefully others will post some comments.

    ReplyDelete
  3. Ya, the evolution of software design left this place in the dust long ago. I've tried more modern stuff, but skipping too many steps doesn't work well here. It's looking like we're going to have to re-discover everything the industry has already discovered, just hopefully at a more accelerated pace :)

    ReplyDelete
  4. This reminds me of one of Brion's posts about why agile can't work in an enterprise. Things generally move too slowly.

    Problems with practices and technology usage can also be a team issue depending on the kinds of developers.

    In terms of your environment you certainly don't want the lazy loading and such to go over 2 boundaries. That would be only asking for trouble. I would think that each request should be responded to with all the data it needs to perform what it was triggered for.

    Yeah the UI should hopefully just receive dummy objects and hit a service or two to accomplish what it wants. A pattern of having the service return DTOs and a UI just generating action requests based on the user's interactions comes to mind. I can't think of the name but basically the UI would just create this requests in response to what the user wanted to do and when it hit a good place to do it, the UI would send a series of the requests back to the server.

    This plays well with what Agatha is doing and a series of action requests could be preformed in a single transaction and share no data very easily and safely given Agatha's request processors and NHibernate's 1st level cache.

    That is probably still not very helpful to you though.

    ReplyDelete
  5. With regards to transactions, given the diverse number of data sources (not all of which are even databases) here, I really couldn't come up with any kind of universal transaction to create a proper unit of work. There's little choice but to create the framework for building atomic actions and have the developer handle transactions and such within those actions.

    I kind of view the whole thing as a great big MVC, with the resulting responses being the "views" and the handlers being the controllers and actions. The models need a little work, though, to have a less procedural approach. Currently the models used are the models returned, and I think I want to change that and split out the DTOs (returned objects) from the "Models" and have the proper models use the repositories as internal services.

    I have some tinkering to do :)

    ReplyDelete