-
Notifications
You must be signed in to change notification settings - Fork 0
Meal Planning - Domain Model #1
Description
From MS Copilot...
Brilliant — since you’ve already got the architecture and you’re using your own DDD framework, we can go straight into the domain model, selection algorithm, and dual‑protein logic in a way that fits cleanly into aggregates, value objects, and domain services.
I’ll keep everything domain‑driven, explicit, and extensible so you can evolve it without painting yourself into a corner.
🧱 1. Domain Model
Below is a clean domain model broken into aggregates and value objects. This is designed to support:
- Ingredient inventory
- Dietary profiles
- Recipe search
- Weekly meal planning
- Dual‑protein variations
- Future extensibility (shopping lists, nutrition, expiry logic)
🍎 Ingredient Aggregate
Ingredient
Represents a single ingredient in your inventory.
Ingredient
- IngredientId
- Name
- Quantity (Quantity)
- Category (IngredientCategory)
- ExpiryDate (optional)
Value Objects
Quantity
- Amount (decimal)
- Unit (UnitOfMeasure)
UnitOfMeasure
- grams, ml, pieces, tbsp, tsp, etc.
IngredientCategory
- vegetable, protein, spice, pantry, dairy, etc.
🥗 Diet Profile Aggregate
DietProfile
Represents a set of dietary rules for a person or group.
DietProfile
- ProfileId
- Name (e.g., "Vegetarian Low-FODMAP", "Omnivore")
- AllowedIngredients (list of IngredientName)
- ExcludedIngredients (list of IngredientName)
- AllowedTags (e.g., vegetarian, low-fodmap)
- ExcludedTags (e.g., garlic, onion)
This lets you express:
- Vegetarian
- Low‑FODMAP
- Omnivore
- Allergies
- Religious restrictions
🍽️ Recipe Aggregate
Recipe
Represents a recipe returned from Spoonacular or stored locally.
Recipe
- RecipeId
- Title
- Ingredients (list of RecipeIngredient)
- Instructions (list of InstructionStep)
- Tags (list of RecipeTag)
- Nutrition (optional)
RecipeIngredient
RecipeIngredient
- Name
- Quantity (Quantity)
- IsProtein (bool)
Marking IsProtein is crucial for dual‑protein logic.
🍱 MealPlan Aggregate
MealPlan
Represents a weekly plan.
MealPlan
- MealPlanId
- WeekStartDate
- Meals (list of PlannedMeal)
PlannedMeal
PlannedMeal
- DayOfWeek
- BaseRecipe (Recipe)
- Variations (list of MealVariation)
MealVariation
MealVariation
- DietProfileId
- ProteinSubstitution (optional)
- AdjustedIngredients (list of RecipeIngredient)
This structure allows:
- One base recipe
- Multiple variations (vegetarian, omnivore)
- Shared sides
- Different proteins
🧠 2. Selection Algorithm (Friday Meal Generation)
This is a domain service, not an aggregate.
MealPlanGeneratorService
Inputs:
- IngredientInventory
- DietProfiles
- RecipeRepository (Spoonacular + cached recipes)
- NumberOfMeals (5)
Output:
- MealPlan
Algorithm Overview
Step 1 — Build Candidate Recipe Pool
For each diet profile:
-
Query Spoonacular with:
- Ingredients you have
- Dietary tags
- Meal type = dinner
-
Filter out recipes that:
- Contain excluded ingredients
- Require ingredients you don’t have (unless small amounts)
- Cannot be adapted to dual‑protein (optional rule)
-
Score each recipe (see scoring below)
Step 2 — Score Recipes
A simple scoring model:
Score =
IngredientMatchWeight * IngredientMatchPercentage
+ PerishableUsageWeight * PerishableUsageScore
+ VarietyWeight * VarietyScore
+ PrepTimeWeight * PrepTimeScore
IngredientMatchPercentage
How many ingredients you already have.
PerishableUsageScore
Higher if it uses ingredients close to expiry.
VarietyScore
Penalise repeating cuisines or proteins.
PrepTimeScore
Optional: prefer < 45 minutes.
Step 3 — Select Top Recipes
Pick the top N recipes per diet profile.
Then merge them into a combined pool.
Step 4 — Dual‑Protein Adaptation
For each candidate recipe:
-
Identify the primary protein
-
Check if:
- The base recipe is vegetarian OR
- The protein is a single ingredient that can be swapped
-
Generate variations:
- Vegetarian version (tofu, tempeh, eggs)
- Omnivore version (chicken, salmon, beef, etc.)
-
Ensure both variations share:
- Sides
- Seasoning
- Cooking method (if possible)
Step 5 — Build the Weekly Plan
Pick 5 meals that:
- Maximise ingredient usage
- Respect dietary profiles
- Provide variety
- Include at least 2–3 dual‑protein meals
Step 6 — Produce Shopping List
Subtract used ingredients from inventory.
Add missing ingredients.
🔀 3. Dual‑Protein Logic (Domain Service)
ProteinSubstitutionService
Inputs:
- Recipe
- DietProfile
- ProteinMappingRules
Output:
- MealVariation
Protein Mapping Rules
Define a mapping table:
ProteinMapping
- BaseProtein: "salmon"
- VegetarianAlternative: "tofu"
ProteinMapping
- BaseProtein: "chicken"
- VegetarianAlternative: "tempeh"
You can also define:
ProteinCategoryMapping
- fish → tofu
- poultry → tempeh
- beef → seitan (if not low-FODMAP)
Substitution Algorithm
- Identify the protein ingredient in the recipe
- Look up a vegetarian alternative
- Replace the protein ingredient
- Adjust cooking instructions:
- Replace “pan‑fry salmon” with “pan‑fry tofu”
- Adjust cooking time
- Keep all sides identical
- Produce two variations:
- Vegetarian
- Omnivore
🧩 Putting It All Together
Your DDD structure will look like:
Aggregates
- Ingredient
- DietProfile
- Recipe
- MealPlan
Domain Services
- MealPlanGeneratorService
- RecipeScoringService
- ProteinSubstitutionService
- IngredientMatchingService
Repositories
- IngredientRepository
- RecipeRepository (Spoonacular + cache)
- MealPlanRepository
If you want, I can help you next with:
- Concrete C# domain models
- Domain service implementations
- A full example of generating a weekly plan
- A dual‑protein substitution engine in code
- A sample JSON structure for Spoonacular responses mapped to your aggregates
Just tell me where you want to go from here.