4
votes

For a multi tenancy eCommerce application, I have given the following ER schema:

Order has_many OrderProduct belongs_to Product belongs_to Price

With Ecto, it would be easy to insert an Order with many OrderProduct at once via cast_assoc(:order_product) in the changeset function of Order.

But since a Product and also a Price may be deleted in the future, I would like to additionally store the value of Price and a quantity in OrderProducts (:price, :quantity).

As far is I know I would have the following options to do that:

  1. In the changeset function of OrderProduct I could fetch Product and join Price by the ID of Product and add :price of Price as change to OrderProduct. This would result in N+1 queries, since I have to execute that query for every OrderProduct in Order (which would be ok in this case since I have only a limited number of Products per Order). But furthermore, I would prefer keeping out the Repo of the schema definition module. But most importantly: I am using different schemas in my multi tenancy setup so I need to know the prefix in the changeset function of OrderProduct to execute the query on the correct prefix.
  2. Iterate OrderProducts outside of the schema definitions in my Orders module and add :price as change to OrderProduct changeset:
%Order{} 
|> Order.changeset(attrs)
|> iterate_order_products_and_map_price_change(tenant)
|> Repo.insert(prefix: tenant)
  1. Use Ecto.Multi where the order is inserted first and then I add the relationships with an extra Ecto.Multi.run

I would prefer the first option since it seems to me that this would be the cleanest approach to do that.

  • Are there any other or better options for this scenario?
  • Would it be possible to preload via join Product and Price for OrderProduct on an insert outside of the schema as well? Then I wouldn't have the N+1 in 1.?
1

1 Answers

2
votes

Ecto.Changeset.prepare_changes/2 is another option and probably the one you are looking for: https://hexdocs.pm/ecto/Ecto.Changeset.html#prepare_changes/2