Some time ago I wondered how writing a larger application would work without any ORM - I started a thread here to gather some input from others and finally decided to give it a try. After first attempts in very small personal projects I set out to create what I would call a medium sized application for use in a network of companies from different locations and its main goal was to manage order processing between car services, car owners, etc., including statistics reports and so on - there were quite a number of intricacies to how it was supposed to work but I won’t go into details since they are not relevant. The application has been up and running for a few months and being constantly worked on with new features so I would like to share my experience how I feel about not using an ORM - and hopefully gain other people’s opinions and insights!
1. Back to plain SQL
As a way of interacting with the database I chose Doctrine DBAL, which has a nice set of utility functions for interacting with databases and is a very light library. It also has functions for query building but I didn’t use them. I actually liked using plain SQL for everything, especially that I chose to discover Postgres and its unique features so abstracting SQL would have been unhelpful. I noticed that even writing simple CRUD statements was not really tedious and I didn’t miss an ORM’s simplicity here.
2. Application Model
For me this was the biggest thing that stood out - without an ORM library I had no models to start with, nothing generated for me. I had to do it all by hand and it made me think really hard about how to write the models and how to structure them. On the plus side I could create them exactly tailored to the needs of my application without any unused redundant stuff.
After investigating a few articles on model architecture I decided that my model layer would be composed of independent service classes that would provide an API for interacting with the outside world - the API would provide just the methods that my application needs. The outside world for the models in this case are mostly controllers (they could be the views also) and the controllers tell the models what to do based on the request data. The models would contain most of the application code.
Now the models would need a way to communicate with the database somehow. The ideal solution, I thought, would be to split the model into two layers:
a) the outer layer containing the main business logic
b) the inner layer containing an abstracted way of communicating with the database
This structure would make my application independent from the way I access the database - I could use plain SQL or I could use an ORM of any kind - the inner layer would translate it all to the proper underlying database library.
However, I didn’t go that route because the amount of work for creating this kind of abstraction didn’t justify the benefits in this case. Switching database access library seemed like an extremely unlikely scenario so I simply chose a shortcut: I created a single model layer that had a dependency on DBAL and it simply used plain SQL via DBAL to interact with the database. So most model classes would require the DBAL object via dependency injection.
3. Fewer objects, back to arrays
Because of lack of ORM the data fetched from and sent to the database are in array format. Using dumb stdClass objects would make no sense in a situation where these objects are not defined anywhere. Therefore, I deal mainly with primitive data structures - this is certainly some drawback but not a huge one. No code completion, etc. however, more freedom to do anything I want with these arrays. I can fetch any additional data using joins, sql functions, stored procedures, etc. without worrying how to hydrate existing stiff ORM objects with them - I just add new columns to fetch, or values, or whatever I like, and the array won’t complain.
Because I’m dealing with primitive data then all necessary data manipulation, formatting, etc. is moved to outside modules (classes) and I use them. Not a problem really and perhaps even an advantage since I’m pushed to build small independent classes with single responsibilities.
4. Simple CRUD
Simple CRUD stuff required a bit of manual coding but it was not that tedious after I had it ready for the first table/model. I used to use a framework that auto-generated the CRUD stuff for me, which was a bonus. I think I might need to think of a code generator for the simplest CRUD.
5. Overall complexity dealing with database
If I were to compare then to me the overall complexity is smaller without an ORM. It’s true that I lose some small conveniences when dealing with simple CRUD but on the other hand I do not have the whole ORM library that I have to deal with, learn and use. Such libraries are not (usually) small and using them also requires time and expertise.
Also, I get rid of all the magic that an ORM does behind the scenes so I found my code became actually clearer. But it must be noted that I like and enjoy SQL, which has always been clearer for me than a bunch of object oriented ORM commands. Not having to think about how to use an ORM with relations is also sweet - just write a join SQL and fetch the necessary data without any problem. Adding new columns to the database is also very simple - no need to deal with XML configurations and the like - just add a column and use it right away.
6. OOP
Paradoxically, I found that without an ORM my code became more object-oriented than before. That is because despite dealing with primitive array data I am more pushed to create all the OO components that deal with the data and behaviour. The data structures became more primitive while the code around them more object-oriented.
7. Final thoughts
To be honest after these months of dealing with ORM-less code I’ve fell in love with this coding style and I feel quite a relief. There are a few minor disadvantages but to me they are outweighed by the greater simplicity and clarity of code. The code is more dependent on the database than with an ORM but this is not a problem in projects that are not meant for database switching. I don’t think this coding style is for everyone since there are certainly people who do not like SQL so much. Personally, I’ll continue with this in my next projects and will try to still improve on some things that might be done better.
And some technical stuff: the project is based on the Silex framework - I wanted something minimalistic that would not get in the way. Actually, I made the application as framework independent as possible so I could easily take all the controllers, models and views and move them to another framework with relative ease. The framework does not matter much.
If anyone has comments or opinions on this then please share. It is always a good idea to hear other approaches even if we don’t choose to follow them!