A lot of e-commerce abstractions get a bit messy around abstracting products, SKUs, product options, product variations and so on. The fact that there doesn’t seem to be a standard or obvious abstraction for this suggests it is tricky to get right.
It might be helpful in a lot of systems to extract a Product Page model as an abstraction. I think this avoids a lot of the trouble as it’s easy to think about and lets your code match your conversations.
The Product Page models a web page (or equivalent app view) that displays things to purchase. With this Product Page entity abstracted out, the Product entity can be much simpler. The Product is then a model of a distinct physical item with a SKU that can be on a shelf in a warehouse or shop. A t-shirt design with three different colours to choose from is three instances of Product.
There’s no need to mess around with any entities such as Product Group, Product Variation, Product Option and so on like you find in many e-commerce abstractions. The Product entity is easy to understand as it corresponds directly to a set of real-world objects. Products can be associated with Product Pages freely in a way that feels quite natural.
This abstraction captures the fact that the business is often most interested in how to display products on a page in a useful way that maximises conversions. It avoids the common trap of trying to capture a perceived taxonomy of objects in the real-world that will often turn out to not be particularly meaningful to the business.
I missed this opportunity when designing the abstractions for Pop Robin Cards back in 2016, instead going with the less-flexible Product and Product Option entities. This would have been better modelled as Product Page and Product, respectively.