February 14, 2025·Product
The power of an elegant abstraction
Harminder Virk
![Harminder avatar](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2F8eb4bbcbdb132011fc5a980dd698b8994f26f553-400x400.jpg%3Fw%3D48%26h%3D48&w=48&q=75)
Harminder Virk
Simple abstractions make developers more efficient by eliminating decision fatigue and lowering maintenance costs.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2Fb4f4984c332a7d04cb6a5372d5977b6ef2058d43-3200x1672.jpg%3Fw%3D1280%26fm%3Dpng&w=3840&q=75)
Medusa is a commerce platform with a built-in framework for customization. Aside from providing developers with tools to build differentiated and unique commerce applications, our framework is designed to make developers more efficient. Building an application involves working with many different tools, and our goal has always been to simplify decision-making by offering simple yet powerful abstractions.
Our Data Modeling API (DML) sits as a layer on top of Mikro-ORM to define database-backed models in a Medusa application. Creating DML was a conscious choice to simplify the data access layer by removing features instead of adding more.
Eliminating decision fatigue
In contrast to ORMs, DML offers a schema-only API with no option to define lifecycle hooks or write business logic within your models. Of course, these features are helpful when you are just using the ORM without any architectural abstractions on top of it.
However, in a Medusa app, we have dedicated places to write business logic in the form of Services. Having another place (e.g., ORM) to write the business logic leads to decision fatigue and inconsistencies in code.
Therefore, DML offers an API that describes only the schema of a database table and provides a clear path toward using Services to manipulate the data or write business logic. This results in an elegant, less verbose, and easy-to-understand API.
Here's a quick comparison of defining the same data models using MikroORM and DML.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2F9a47b0864434910e61f051c1fcfa9a96aa0e3cfd-2944x11632.png%3Fw%3D1280&w=3840&q=75)
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2F044d5bc48c62aff041a8e6a12427804d0248abb2-2944x4032.png%3Fw%3D1280&w=3840&q=75)
Opinionated choices to reduce cognitive load
Every codebase strives for consistency, deduplication, and enforced systems to prevent running into common pitfalls. A great way to achieve this is by being opinionated about how we do things, and a step further could be building abstractions that lock down these opinions in code.
When initially using MikroORM to define the data models in our codebase, we pinned down our decisions around defining relationships, using ULIDs, making every model soft deletable, and always defining certain indexes. Enforcing these decisions was a manual process of reviewing PRs and ensuring that everyone adhered to the same pre-defined rules.
However, with DML, we encoded all these decisions as part of the default behavior and eliminated the risk of diverging from our opinions.
In the following example, we use a custom filter and define the Copy to clipboarddeleted_at
property to enable soft-deletes in MikroORM.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2F69ebc4d4b146753176d351c4a6dde5fe55e2cfdf-2944x1792.png%3Fw%3D1280&w=3840&q=75)
Here, we define the necessary hooks to use client-generated ULIDs.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2Fe302322da44cda572a61148527898dacd40a20d4-2944x2432.png%3Fw%3D1280&w=3840&q=75)
Finally, define the properties for the created_at and updated_at timestamps.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2Fb4c43bba2cff6293fc71a3a3f00b51ec9d71a1b8-2944x2112.png%3Fw%3D1280&w=3840&q=75)
Meanwhile, with DML, we only have to define the model and get client-generated ULIDs, timestamps, and soft deletion.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2F904be25818bef7cf7dce3ac53430fdb6290a1b01-2944x1072.png%3Fw%3D1280&w=3840&q=75)
Let's take another example of defining a Copy to clipboardManyToOne
(aka Copy to clipboardbelongsTo
) relationship. In MikroORM, we have to define the foreign key property and relationship name and also define the necessary hooks to populate the foreign key when it is not set explicitly.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2Fd7a8b575cea0324834873e04f17a722370c8b078-2944x3152.png%3Fw%3D1280&w=3840&q=75)
All these moving parts are encapsulated within the relationships API of DML.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2F75a9a6b92372bcf4234c8d8490edcdba1655d60d-2944x1312.png%3Fw%3D1280&w=3840&q=75)
Fruits of maintenance
The decision to create an opinionated abstraction without leaking Mikro-ORM's internals at the DML level proved a significant step towards future maintainability while minimizing the impact of breaking changes on our users.
We initially developed DML on top of MikroORM v5 and very recently migrated to MikroORM v6. There were many breaking changes between Copy to clipboardv5 -> v6
, but we absorbed all of them within the DML layer, resulting in a smoother upgrade for our users.
Our Data Model API is one of many tools in our framework designed to help developers focus on shipping features that add value instead of dealing with unnecessary decisions and boilerplate code. This in turn means faster development, shorter implementation timelines, and minimal maintenance.
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2F9a47b0864434910e61f051c1fcfa9a96aa0e3cfd-2944x11632.png%3Fw%3D1280&w=3840&q=75)
![Image modal](/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2F5a711ubd%2Fproduction%2F044d5bc48c62aff041a8e6a12427804d0248abb2-2944x4032.png%3Fw%3D1280&w=3840&q=75)