ORMs and other approaches to map domain models to physical DB models
By Nikita Kostomarov (ndkostomarov@edu.hse.ru)
Introduction
Nowadays programming languages and libraries written for them allow developers to use so called code-first approach when working with relational databases. It is possible thanks to Object-relational mapping (ORM) which connects domain entities written by developers as a part of project and physical models stored in databases.
Main part
Firstly, let us look at what possibilities ORM has to offer to developers.
ORM allows developers to not only create migrations for creating and altering tables in relational databases on domain models changes, yet also they provide a possibility to write code queries that will be mapped to the query language used in database. It simplifies getting and modifying data in DB and lets developers not to think about writing migrations or queries which saves some time when working on project.
For example, ORMs are useful when developers come up with DB architecture using DDD approach and simply write classes based on entity relation diagram which was designed during domain analysis. After that using ORM they can simply initialize database with all the necessary tables and constraints without writing a single line of code in SQL. Moreover, when domain models change during the development of the project ORM will update DB structure to conform new domain diagram.
CRUD operations are also supported in ORMs so that when you need to query some data from DB you can operate with entity classes used for communication with databases. What is more, developers do not have to know SQL to query entities when using ORMs.
Despite rich possibilities ORMs provide, there are some downsides of this approach. First one is that when you query some data your code has to be compiled in SQL query which takes more time than just using prepared queries written during the development. Although, ORMs also allow you to use «hard-coded» queries or precompile queries before their usage in runtime. Another disadvantage is that some ORMs may be followed by an overheads such as tracking entities when you want to change existing physical model somehow [1]. In addition, sometimes to delete or update a row (or multiple rows) in DB you will have to query entity (or list of them) related to it and then tell database context provider to delete or update it using primary key which might be inconvenient when you need to delete or update multiple rows using non-primary key columns. Lastly, not all the ORMs provide you with ability to create table without primary key, which might be the case in some domains [2].
Alternatives
Alternative approaches for ORMs are Object-document mapping (ODM) [3] and handling mapping by yourself (practically it will be hand-written ORM anyway). ODM is basically an ORM used for working with document-based databases such as MongoDB, it maps domain models in documents that will be stored in DB. Hand-written mapping for models is useful when you use hand-written SQL queries for CRUD operations to gain full control of how to query, modify or delete data.
There are also so-called micro-ORMs [4] such as Dapper for .NET. They provide developers with ability to map query results in the domain models so they do not need to map them by themselves.
As hand-written mapping and utilization of micro-ORMs are basically the same things I will analyze them as a single alternative to full ORMs.
ODM
As I mentioned earlier, ODMs are practically ORMs but ones intended to work with NoSQL document-based databases. However, they do not have a feature that allows you to add such constraints as foreign keys in terms of relational databases simply because document-based DBs do not support validation of attribute that reference another collection, though, you can do it by querying documents from referenced collection by yourself if you need to.
Another difference between ODMs and ORMs is that former use document schemes to define data structure in collections whilst latter creates strongly typed tables.
Micro-ORM
When you want to gain full control of how data will be queried, modified or deleted in the DB you can utilize micro-ORMs.
They allow you to use hand-written queries for communication with database handling mapping of physical models in the domain entities by themselves. However, developers will have to define database structure by writing SQL code when applying migrations. This may lead to certain problems when domain models change over time while developing project because changing DB structure still has to be done using hand-written migrations. In small projects it might be even more convenient rather than using full ORMs as you have small domain and can handle changes over time by yourself. But in bigger projects when there are possibilities of changing team members during the development it can cause problems when onboarding new workers as they will have to take some time to look up to all the queries written in SQL to understand logic of working with DB.
On the other hand, hand-written queries provide you with ability to update and delete multiple rows using single query rather than fetching data from DB first and then modifying or deleting it by primary key. It lowers database load and speed up methods execution which is always a positive effect for system overall.
Another positive side of using micro-ORMs is that you define queries during the development so that they will not be compiled during runtime which also decreases system load.
One more downside of this approach is testability. With micro-ORMs you will have to use real database for testing whilst utilizing full ORM you will be able to use in-memory database provider [5].
Conclusion
ORMs definitely simplify developing projects where DB is used. Personally I worked with ORM Entity Framework and micro-ORM Dapper when developed different .NET back-end applications and in my opinion both approaches have their use cases when it is convenient to utilize one of them (full ORMs for big projects and micro-ORMs - for small ones). ODM is quite similar to ORM way to map models which is applicable when working with certain databases as well.
References
[1] Tracking vs. No-Tracking Queries https://learn.microsoft.com/en-us/ef/core/querying/tracking
[2] Hibernate documentation https://docs.jboss.org/hibernate/core/3.5/reference/en/html/mapping.html#mapping-declaration-id
[3] ODM vs ORM https://tangledguy.hashnode.dev/odm-vs-orm
[4] What is Micro ORM? https://gunnarpeipman.com/micro-orm/
[5] Testing without your production database system https://learn.microsoft.com/en-us/ef/core/testing/testing-without-the-database