А в чем проблема добавлять в заказ кофе отдельно, а топинг отдельно?
На самом деле не зватает данных о том. как вы собираетесь это всё запрягать.
Лучше сохранить плоскую простую структуру, а топинги просто указывать сразу после кофе.
В одном заказе может быть несколько кофе с разными топингами.
Вы можете сделать вместо ManyToMany модель произведения Заказе на Продукт в ручную:
ПродуктВЗаказе(Заказ, Продукт, порядковый_индекс).
Но вот я вижу у вас в Продукте какой-то volume. Это что значит, капучино у вас представлено в таблице продуктов всеми доступными объёмами? А топинги тоже определены объёмами?
Если так, то Продукт у вас абстрактен, а в заказе он представлен таблицей ПродуктВЗаказе.
Благодаря упорядоченности этой таблицы можно условиться, что если Продукт дополнительный, то он должен идти после продукта. в который его добавляют. Это чтобы по Заказу можно было однозначно приготовить кофе и не перепутать топинги.
Минусов такого подхода несколько:
- нужны методы перестановки элементов заказа, чтобы, например, заменить топинг к первому кофе из нескольких указанных в заказе.
- если дополнитеьный продукт вроде топинга может оказаться и основным продуктом, то флаг его дополнительности придётся указывать в модели РодуктыВЗаказе. К примеру, можно заказать лимон и текилу по отдельности, а можно лимон в качестве добавки в чай. В этом случае нельзя указать в Продукте "Лимон" атрибут "дополнительный", ну или придется заводить отдельные Продукты для одного и того же лимона.
Я бы не парился с признаком "дополнительности" у Продуктов, а вынес это флаг в таблицу ПродуктыВЗаказе и сделал бы методы перестановки элементов.
Тогда заказ бы у меня был плоский, но выглядел как-то так:
Капучино 300мл Арабика
+ Карамель
+ Корица
+ Шоколад
Эспрессо 400мл Арабика
+ Коньяк
Тогда, если девушка скажет: "ой. а можно мне ещё мятный сироп туда?"
Бариста в приложении добавит "+ Мятный сироп" и перетащит его над Эсрессо.
Плюсик будет определяться флагом в модели ПродуктыВЗаказе, а позиция целочисленным индексом в этой модели.
Индексы можно проставлять с шагом, например, в 1000, а при перестановке тогда просто апдейтить только переставляемую запись устанавливая индекс посерединке между любыми двумя соседними.