Primsa.io and Expanding my DB Model with sub-types
Right now I have this pretty awesome and fully featured app built on Prisma@1.34.0. As part of trying to be LEAN and get it running quickly I have purposely only focused on a single Product Type, Flight Controllers
. The plan was never to stop there, but expand to other Types like: Motors, Receivers, Cameras, and maybe 10 other product types. I'm now at that point where it's time to expand and I am waffling on how to do this with the Prisma DataModel definitions available.
Let me start by showing you my current datamodel.prisma
. I wanted to prune it down to reveal only what's relevant to the question but I worry in doing so, I take away context that could be important. So I apologize for it's length but here it is:
The Problem I ran into with just adding a new type Motor{}
is when it comes to linking to the type MerchantLinks
. A MerchantLink
is essentially a way to attach a FlightController
to a Merchant(store)
with fields like price
, link
, availability
. And as part of my DB Model I have it setup with relationships in both ways, with the purpose of being able to go to a Merchant page and see all of the products they offer. This means having a field called flightController
on type FlightControllerMerchantLink
. I cannot rename these to a product
field on type ProductMerchantLink
because product
cannot be setup as product: FlightController || Motor || Battery || Camera || ...
. I can only point it at a single type.
Ok, so I'm not DB Design Engineer, and it's probably one of my weaker engineering skills so this is where I turn to colleagues and the internet. In doing so, as far as I can tell I have two options to get around this.
Option 1, Duplicate the FlightControllerMerchantLink Type for all product types
I think I could create a bunch of the type {ProductType}MerchantLink
, one for each product type. And then link all of those as optional fields on the merchant, like so:
The thing I dislike about this approach is that it's not normalized. I'm repeating columns across many tables, such price, url, inStock, postedBy, etc
repeated across all of the {ProductType}MerchantLink
. Also, I'm not sure if this will have any ramifications that will block me from doing what I want to achieve in terms of site products, pages, or features. I've not taken this path yes, as I opted for option 2 instead. So I'll describe that now.
Option 2, Replace Flight Controller with Product and have a bunch of optional fields to store all the product-type-specific data
After talking with my co-worker he mentioned that he's solved this type of problem before by having a single type Product
with a field of Type
which describes what type it is, and then bunch of fields which point to the specific ProductType, where only one would be set at a time. Granted he did not implement something like this in Prisma, but I think it could still work. Like so:
With this setup, all of the linked Types
for things common to all products, all link off of the type Product
. These things include: Images, Product Reviews, Ratings, and Merchant Links. All of the fields common to all products can be added directly like cheapestPrice, averageReview, createdAt, etc.
I set about testing this new schema with a single type, FlightController
and it seemed to work. So I pushed forward and expanded to 10 different product types.
Here's how my data model looks now
And here's my mutation resolvers for adding/editing products. NOTE: The code definitely needs cleaned up but I got it to a point that it works!
One of the benefits of going this route of making everything a base Type Product
is that I can add/edit/delete any product type with a single mutation endpoint. I was also able to update my FE code for all of the pages I had setup for just flight-controllers to be retrofitted to work with the other product types.
This includes:
- Product List Page
- Product Details Page
- Product Edit Page
- Product Search Component
- Add/Edit Images Component
- Add/Edit Reviews Component
I'm not sure if this approach will cause issues down the road but it seems to be working for now.
One thing that I thought for sure wouldn't work but was pleasantly surprised on, was that my product list page queries still work, with a little modification. I'm able to sort/filter at the Product and Product.SubType (Product.flightController) levels in a single Prisma query like so. A key thing to note here is that I'm doing an orderBy
at the Product
level but I'm doing filtering where
clause at the Product.flightController
level.
Fast forward 2 weeks and this approach is still going strong. I created a new feature which allows the user to build an entire drone attaching all of the common components such as Frame, Motors, Flight Controller. The components are all of type Product
, with product-specific sub-types. I plan to continue heading down this path and I'll report back if I run into any issues.