Why another ORM?

      No Comments on Why another ORM?

I love reusing code as much as the next fellow and I have used a number of existing ORM solutions extensively. There’s the great and absolutely valid Zend_Db component from Zend Framework, and ORM from Kohana or even Doctrine for those who want something stand-alone and robust. So, if there are all these other solutions out there, why did I ever go and build my own?

I guess my ultimate inspiration came from Jeff Atwood. The beginning of this story goes into the history of Gacela’s development (another topic for another day) because Gacela certainly isn’t my first time at this particular rodeo and I’ll be surprised if it’s my last. The one nice thing about doing it yourself, is that you get to learn about all the common mistakes (faulty assumptions, stupid assumptions) that eventually box in the design and then you get to start all over again with the process. Suffice it to say that between using other people’s ORM’s and my own, I found a number of hurdles with the existing solutions that I wanted to get past:

  • Manually building out information such as relationships between entities that my database already knew about. Why couldn’t the ORM just figure it out from there?
  • Table inheritance issues such as how do I perform a find() operation and have it return back two different models based on a role or type
  • Separation of data access from business logic. Simply put, most ORM’s all use the Active Record pattern to combine data access with business logic in the same object. I want to use the more robust Data Mapper pattern in my applications.
  • Personally, I don’t believe that doc blocks should be used for programmatic purposes.
  • My boss kept suggesting that at some point we’d have to ditch the ORM and go to straight queries with hard-coded checks against a cache (memcache for instance). I kept thinking, there has to be a way to combine the versatility of a object-oriented framework for data mapping with the scalability of caching.
  • In my opinion, domain level code (controllers, models) should not contain database queries rather they should all exist at the data mapper level.
  • I think that in the long-run, the best thing for the PHP community as a whole will not be “The one framework to rule them all” but rather if we have a number of small highly specific frameworks that integrate well with each other and we allow framework developer’s to focus on the features within their highly specific framework rather than trying to make just another generic, do everything framework.
  • The default functionality (by convention) should be easy to use with relatively little setup. But I should be able to completely bypass the defaults with ease and go bare metal if need be.
  • In Patterns of Enterprise Application Architecture, Fowler suggested that Data Mappers should be able to map data from any data source. This includes Xml, RESTful web services, a database, or anything else that might come our way. I have yet to have found a solution that stepped into the dubious territory of supporting a non-database backed ORM.

To this end, I started Gacela from the ground up to meet these lofty requirements:

  • Whenever possible, the DataSource pulls in relationship and field metadata so that you don’t have to hand-code that information
  • Data Mappers support Concrete Inheritance, Association relationships, and Dependent Relationships out of the box.
  • Mappers contain all data access logic. Models contain only business logic.
  • Uh, yeah. Doc blocks just explain what’s going on.
  • Mappers, Models, and Resources are all fully serializable because only the DataSources and the core Gacela class contain non-serializable resources. As a result it was a cinch to implement storing these items in a file system cache like memcache.* Models and Collections of Models only have to reloaded from the DataSource as a result of insert, update, or delete operations.
  • If you want to find all entities by a shared piece of data, you can build a Criteria object. But if you want to build a custom query to pull information from the DataSource, you’ll have to implement a custom find method in the Mapper. This means that you could theoretically switch out your DataSource with minimal impact on the domain layer (Models, Controllers) because they don’t contain any logic that ties to a particular data structure or DataSource.
  • Gacela is completely stand-alone. And its required components come with a stock PHP 5.3 LAMPP server (except memcache of course).
  • If you look through the documentation, you should see how simple it is to setup a Mapper and Model based on a database table just like you would with standard ORM’s. You should also see how the Mapper can pick up standard relationships and plug them in for you. Lastly, you should notice how quickly you can override the defaults to work things however you need.
  • Even though only MySQL is currently implemented, the entire framework is designed to separate the Models and Mappers from a specific type of DataSource. I fully anticipate implementing MSSQL and REST support very soon.
  • Taking this idea even further, Mappers and Resources don’t change from request to request because they represent objects that are more or less static. Its not like you’re going to change the structure of your DataSources mid-request. Therefore, the only time your application has to reload these objects into memory is when you do an application update that changes a Mapper or Resource.

So, why did I decide to build just another ORM? I guess its because I agree with Jeff:

Indeed. If anything, “Don’t Reinvent The Wheel” should be used as a call to arms for deeply educating yourself about all the existing solutions — not as a bludgeoning tool to undermine those who legitimately want to build something better or improve on what’s already out there. In my experience, sadly, it’s much more the latter than the former. So, no, you shouldn’t reinvent the wheel. Unless you plan on learning more about wheels, that is.

Leave a Reply

Your email address will not be published. Required fields are marked *