# Multi-Household & Multi-Store Architecture Plan ## Executive Summary This document outlines the architecture and implementation strategy for extending the application to support: 1. **Multiple Households** - Users can belong to multiple households (families, roommates, etc.) 2. **Multiple Stores** - Households can manage lists for different store types (Costco, Target, Walmart, etc.) ## Current Architecture Analysis ### Existing Schema ```sql users (id, username, password, name, role, display_name) grocery_list (id, item_name, quantity, bought, item_image, image_mime_type, added_by, modified_on) grocery_history (id, list_item_id, quantity, added_by, added_on) item_classification (id, item_type, item_group, zone, confidence, source) ``` ### Current Limitations - **Single global list** - All users share one grocery list - **No household concept** - Cannot separate different families' items - **Store-specific zones** - Classification system assumes Costco layout - **Single-level roles** - User has same role everywhere (cannot be admin in one household, viewer in another) --- ## Design Considerations & Trade-offs ### Key Questions to Resolve #### 1. Item Management Strategy **Option A: Shared Item Master (Recommended)** - ✅ **Pro**: Single source of truth for item definitions (name, default image, common classification) - ✅ **Pro**: Consistent item naming across households - ✅ **Pro**: Can build item recommendation system across all households - ✅ **Pro**: Easier to implement smart features (price tracking, common items) - ❌ **Con**: Requires careful privacy controls (who can see which items) - ❌ **Con**: Different households may classify items differently **Option B: Per-Household Items** - ✅ **Pro**: Complete household isolation - ✅ **Pro**: Each household fully controls item definitions - ✅ **Pro**: No privacy concerns about item names - ❌ **Con**: Duplicate data across households - ❌ **Con**: Cannot leverage cross-household intelligence - ❌ **Con**: More complex to implement suggestions **Option C: Hybrid Approach (RECOMMENDED)** - ✅ **Pro**: Best of both worlds - ✅ **Pro**: Shared item catalog with household-specific classifications - ✅ **Pro**: Privacy-preserving (only households share item usage, not personal data) - **How it works**: - Global `items` table (id, name, default_image, created_at) - Household-specific `household_list` table references item + household - Each household can override classifications per store --- ## Proposed Schema Design ### New Tables ```sql -- Households (e.g., "Smith Family", "Apartment 5B") CREATE TABLE households ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT NOW(), created_by INTEGER REFERENCES users(id), invite_code VARCHAR(20) UNIQUE NOT NULL, -- Random code for inviting users code_expires_at TIMESTAMP -- Optional expiration ); -- Store Types (e.g., "Costco", "Target", "Walmart") CREATE TABLE stores ( id SERIAL PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE, default_zones JSONB, -- Store-specific zone layout created_at TIMESTAMP DEFAULT NOW() ); -- User-Household Membership with per-household roles CREATE TABLE household_members ( id SERIAL PRIMARY KEY, household_id INTEGER REFERENCES households(id) ON DELETE CASCADE, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, role VARCHAR(20) NOT NULL CHECK (role IN ('admin', 'user')), joined_at TIMESTAMP DEFAULT NOW(), UNIQUE(household_id, user_id) ); -- Household-Store Relationship (which stores does this household shop at?) CREATE TABLE household_stores ( id SERIAL PRIMARY KEY, household_id INTEGER REFERENCES households(id) ON DELETE CASCADE, store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE, is_default BOOLEAN DEFAULT FALSE, -- Default store for this household UNIQUE(household_id, store_id) ); -- Master Item Catalog (shared across all households) CREATE TABLE items ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, default_image BYTEA, default_image_mime_type VARCHAR(50), created_at TIMESTAMP DEFAULT NOW(), usage_count INTEGER DEFAULT 0 -- For popularity tracking ); -- Household-specific grocery lists (per store) CREATE TABLE household_lists ( id SERIAL PRIMARY KEY, household_id INTEGER REFERENCES households(id) ON DELETE CASCADE, store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE, item_id INTEGER REFERENCES items(id) ON DELETE CASCADE, quantity INTEGER NOT NULL DEFAULT 1, bought BOOLEAN DEFAULT FALSE, custom_image BYTEA, -- Household can override item image custom_image_mime_type VARCHAR(50), added_by INTEGER REFERENCES users(id), modified_on TIMESTAMP DEFAULT NOW(), UNIQUE(household_id, store_id, item_id) -- One item per household+store combo ); -- Household-specific item classifications (per store) CREATE TABLE household_item_classifications ( id SERIAL PRIMARY KEY, household_id INTEGER REFERENCES households(id) ON DELETE CASCADE, store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE, item_id INTEGER REFERENCES items(id) ON DELETE CASCADE, item_type VARCHAR(50), item_group VARCHAR(100), zone VARCHAR(100), confidence DECIMAL(3,2) DEFAULT 1.0, source VARCHAR(20) DEFAULT 'user', created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), UNIQUE(household_id, store_id, item_id) ); -- History tracking (who added what, when, to which household+store list) CREATE TABLE household_list_history ( id SERIAL PRIMARY KEY, household_list_id INTEGER REFERENCES household_lists(id) ON DELETE CASCADE, quantity INTEGER NOT NULL, added_by INTEGER REFERENCES users(id), added_on TIMESTAMP DEFAULT NOW() ); ``` ### Indexes for Performance ```sql -- Household member lookups CREATE INDEX idx_household_members_user ON household_members(user_id); CREATE INDEX idx_household_members_household ON household_members(household_id); -- List queries (most common operations) CREATE INDEX idx_household_lists_household_store ON household_lists(household_id, store_id); CREATE INDEX idx_household_lists_bought ON household_lists(household_id, store_id, bought); -- Item search CREATE INDEX idx_items_name ON items(name); CREATE INDEX idx_items_usage_count ON items(usage_count DESC); -- Classification lookups CREATE INDEX idx_household_classifications ON household_item_classifications(household_id, store_id); ``` --- ## Role System Redesign ### Dual-Role Hierarchy: System-Wide + Household-Scoped ```typescript // System-wide roles (app administration) users { id, username, password, name, display_name, role: 'system_admin' | 'user' // Kept for app-wide controls } // Household-scoped roles (per-household permissions) household_members { household_id, user_id, role: 'admin' | 'user' } ``` ### System-Wide Role Definitions | Role | Permissions | |------|-------------| | **system_admin** | Create/delete stores globally, view all households (moderation), manage global item catalog, access system metrics, promote users to system_admin | | **user** | Standard user - can create households, join households via invite, manage own profile | ### Household-Scoped Role Definitions | Role | Permissions | |------|-------------| | **admin** | Full household control: delete household, invite/remove members, change member roles, manage stores, add/edit/delete items, mark bought, upload images, update classifications | | **user** | Standard member: add/edit/delete items, mark bought, upload images, update classifications, view all lists | ### Role Transition Plan **Migration Strategy:** 1. Create default household "Main Household" 2. Migrate all existing users → household_members (old admins become household admins, others become users) 3. Keep existing `users.role` column, update values: - `admin` → `system_admin` (app-wide admin) - `editor` → `user` (standard user) - `viewer` → `user` (standard user) 4. Migrate grocery_list → household_lists (all to default household + default store) 5. Migrate item_classification → household_item_classifications --- , systemRole } // System-wide role req.household = { id, name, role } // Household-scoped role req.store = { id, name } // Active store context ### Authentication Context **Before:** ```javascript req.user = { id, username, role } ``` **After:** ```javascript req.user = { id, username } req.household = { id, name, role } // Set by household middleware req.store = { id, name } // Set by store middleware ``` ### Middleware Chain with systemRole) router.use(auth); // 2. Household middleware (validates household access, sets req.household with householdRole) router.use('/households/:householdId', householdAccess); // 3. Household role middleware (checks household-scoped permissions) router.post('/add', requireHouseholdRole(['user', 'admin']), controller.addItem); // 4. Admin-only household operations router.delete('/:id', requireHouseholdRole(['admin']), controller.deleteHousehold); // 5. System admin middleware (for app-wide operations) router.post('/stores', requireSystemRole('system_admin'), controller.createStore // 3. Role middleware (checks household-specific role) rouSystem Administration (system_admin only) GET /api/admin/stores // Manage all stores POST /api/admin/stores // Create new store type PATCH /api/admin/stores/:id // Update store DELETE /api/admin/stores/:id // Delete store (if unused) GET /api/admin/households // View all households (moderation) GET /api/admin/items // Manage global item catalog GET /api/admin/metrics // System-wide analytics // Household Management (any user can create) GET /api/households // Get all households user belongs to POST /api/households // Create new household (any user) GET /api/households/:id // Get household details PATCH /api/households/:id // Update household (admin only) DELETE /api/households/:id // Delete household (admin only) // Household Members GET /api/households/:id/members // List members (all roles) POST /api/households/:id/invite // Generate/refresh invite code (admin only) POST /api/households/join/:inviteCode // Join household via invite code (joins as 'user') PATCH /api/households/:id/members/:userId // Update member role (admin only) DELETE /api/households/:id/members/:userId // Remove member (admin only, or self) // Store Management GET /api/stores // Get all available store types GET /api/households/:id/stores // Get stores for household POST /api/households/:id/stores // Add store to household (admin only) DELETE /api/households/:id/stores/:storeId // Remove store from household (admin only) // Store Management GET /api/stores // Get all available stores POST /api/stores // Create custom store (system admin) GET /api/households/:id/stores // Get stores for household POST /api/households/:id/stores // Add store to household (admin+) DELETE /api/households/:id/stores/:storeId // Remove store (admin+) // List Operations (now scoped to household + store) GET /api/households/:hId/stores/:sId/list // Get list POST /api/households/:hId/stores/:sId/list/add // Add item PATCH /api/households/:hId/stores/:sId/list/:itemId // Update item DELETE /api/households/:hId/stores/:sId/list/:itemId // Delete item POST /api/households/:hId/stores/:sId/list/:itemId/buy // Mark bought // Item Suggestions (across user's households) GET /api/items/suggestions?q=milk // Search master catalog // Classifications (per household + store) GET /api/households/:hId/stores/:sId/classifications/:itemId POST /api/households/:hId/stores/:sId/classifications/:itemId ``` --- ## React Context Refactoring Pattern ### Current Pattern (To Be Replaced) ```jsx // Bad: Context is exported, consumers use it directly export const AuthContext = createContext(null); export function AuthProvider({ children }) { const [user, setUser] = useState(null); return ( {children} ); } // Consumer must import context and useContext import { useContext } from 'react'; import { AuthContext } from '../context/AuthContext'; function MyComponent() { const { user, setUser } = useContext(AuthContext); // ... } ``` ### New Pattern (Best Practice) ```jsx // Good: Context is internal, custom hook is exported const AuthContext = createContext(null); // Not exported! export function AuthProvider({ children }) { const [user, setUser] = useState(null); const [token, setToken] = useState(null); const login = (userData, authToken) => { setUser(userData); setToken(authToken); }; const logout = () => { setUser(null); setToken(null); }; return ( {children} ); } // Export custom hook instead export function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within AuthProvider'); } return context; } // Consumer usage - clean and simple import { useAuth } from '../context/AuthContext'; function MyComponent() { const { user, login, logout } = useAuth(); // ... } ``` ### Benefits 1. **Encapsulation** - Context implementation is hidden, only the hook is public API 2. **Type Safety** - Can add TypeScript types to the hook return value 3. **Validation** - Hook can check if used within provider (prevents null errors) 4. **Cleaner Imports** - One import instead of two (`useContext` + `Context`) 5. **Easier Refactoring** - Can change context internals without affecting consumers 6. **Standard Pattern** - Aligns with React best practices and popular libraries ### Implementation Plan **Existing Contexts to Refactor:** - `AuthContext` → `useAuth()` - `SettingsContext` → `useSettings()` - `ConfigContext` → `useConfig()` (if still used) **New Contexts to Create:** - `HouseholdContext` → `useHousehold()` - `StoreContext` → `useStore()` **Migration Steps:** 1. Keep old context export temporarily 2. Add custom hook export 3. Update all components to use hook 4. Remove old context export 5. Make context `const` internal to file --- ## Frontend Architecture Changes ### Context Structure ```typescript // AuthContext - User identity { user: { id, username, display_name, systemRole }, token: string, login, logout, isSystemAdmin: boolean // Computed from systemRole } // HouseholdContext - Active household + household role { activeHousehold: { id, name, role }, // role is 'admin' or 'user' households: Household[], switchHousehold: (id) => void, createHousehold: (name) => void, joinHousehold: (code) => void, isAdmin: boolean // Computed helper: role === 'admin' } // StoreContext - Active store { activeStore: { id, name }, householdStores: Store[], allStores: Store[], // Available store types (for adding) switchStore: (id) => void, addStore: (storeId) => void // Admin+ onlyme, role }, households: Household[], switchHousehold: (id) => void, createHousehold: (name) => void, joinHousehold: (code) => void } // StoreContext - Active store { /admin → System admin panel (system_admin only) /admin/stores → Manage store types /admin/households → View all households /admin/items → Global item catalog activeStore: { id, name }, householdStores: Store[], switchStore: (id) => void } (Owner) {user.systemRole === 'system_admin' && ( )} ``` **Store Tabs** (Within Household) ```tsx Costco Target Walmart {(isAdmin || isOwner) && + Add Store} → User settings (personal) ``` ### UI Components **Household Switcher** (Navbar) ```tsx ``` **Store Tabs** (Within Household) ```tsx Costco Target Walmart + Add Store ``` --- ## Migration Strategy ### Phase 1: Database Schema (Breaking Change) **Step 1: Backup** ```bash pg_dump grocery_list > backup_$(date +%Y%m%d).sql ``` **Step 2: Run Migrations** ```sql -- 1. Create new tables CREATE TABLE households (...); CREATE TABLE household_members (...); -- ... (all new tables) -- 2. Create default household INSERT INTO households (name, created_by, invite_code) VALUES ('Main Household', 1, 'DEFAULT123'); -- 3. Migrate users → household_members INSERT INTO household_members (household_id, user_id, role) SELECT 1, id, CASE WHEN role = 'admin' THEN 'admin' -- Old admins become household admins ELSE 'user' -- Everyone else becomes standard user END FROM users; -- 4. Create default store INSERT INTO stores (name, default_zones) VALUES ('Costco', '{"zones": [...]}'); -- 5. Link household to store INSERT INTO household_stores (household_id, store_id, is_default) VALUES (1, 1, TRUE); -- 6. Migrate items INSERT INTO items (name, default_image, default_image_mime_type) SELECT DISTINCT item_name, item_image, image_mime_type FROM grocery_list; -- 7. Migrate grocery_list → household_lists INSERT INTO household_lists (household_id, store_id, item_id, quantity, bought, added_by, modified_on) SELECT 1, -- default household 1, -- default store i.id, gl.quantity, gl.bought, gl.added_by, gl.modified_on FROM grocery_list gl JOIN items i ON LOWER(i.name) = LOWER(gl.item_name); -- 8. Migrate classifications INSERT INTO household_item_classifications (household_id, store_id, item_id, item_type, item_group, zone, confidence, source) SELECT 1, 1, i.id, ic.item_type, ic.item_group, ic.zone, ic.confidence, ic.source FROM item_classification ic JOIN grUpdate system roles (keep role column) UPDATE users SET role = 'system_admin' WHERE role = 'admin'; UPDATE users SET role = 'user' WHERE role IN ('editor', 'viewer'); -- 11. Drop old tables (after verification!) -- DROP TABLE grocery_history; -- DROP TABLE item_classification; -- DROP TABLE grocery_listousehold_list_id, quantity, added_by, added_on) SELECT hl.id, gh.quantity, gh.added_by, gh.added_on FROM grocery_history gh JOIN grocery_list gl ON gh.list_item_id = gl.id JOIN items i ON LOWER(i.name) = LOWER(gl.item_name) JOIN household_lists hl ON hl.item_id = i.id AND hl.household_id = 1 AND hl.store_id = 1; -- 10. Drop old tables (after verification!) -- DROP TABLE grocery_history; -- DROP TABLE item_classification; -- DROP TABLE grocery_list; -- ALTER TABLE users DROP COLUMN role; ``` ### Phase 2: Backend API (Incremental) 1. ✅ Create new models (households, stores, household_lists) 2. ✅ Create new middleware (householdAccess, storeAccess) 3. ✅ Create new controllers (households, stores) 4. ✅ Add new routes alongside old ones 5. ✅ Update list controllers to be household+store aware 6. ✅ Deprecate old routes (return 410 Gone) ### Phase 3: Frontend UI (Incremental) 1. ✅ **Refactor Context Pattern** (applies to all contexts) - Move `createContext` inside component files (not exported) - Export custom hooks instead: `useAuth()`, `useHousehold()`, `useStore()`, `useSettings()` - Consumers use hooks directly instead of `useContext(ExportedContext)` 2. ✅ Create HouseholdContext with `useHousehold()` hook 3. ✅ Create StoreContext with `useStore()` hook 4. ✅ Refactor existing AuthContext to use custom `useAuth()` hook 5. ✅ Refactor existing SettingsContext to use custom `useSettings()` hook 6. ✅ Add household switcher to navbar 7. ✅ Create household management pages 8. ✅ Add store tabs to list view 9. ✅ Update all API calls to use household + store IDs 7. ✅ Add invite system UI 8. ✅ Update settings page to show household-specific settings --- ## Advanced Features (Future) ### 1. Item Sharing & Privacy **Levels:** - **Private**: Only visible to your household - **Public**: Available in global item catalog - **Suggested**: Anonymously contribute to shared catalog ### 2. Smart Features **Cross-Household Intelligence:** - "10,000 households buy milk at Costco" → suggest classification - "Items commonly bought together" - Price tracking across stores - Store-specific suggestions **Household Patterns:** - "You usually buy milk every 5 days" - "Bananas are typically added by [User]" - Auto-add recurring items ### 3. Multi-Store Optimization **Store Comparison:** - Track which items each household buys at which store - "This item is 20% cheaper at Target" - Generate shopping lists across stores **Route Optimization:** - Sort list by store zone - "You can save 15 minutes by shopping in this order" ### 4. Enhanced Collaboration **Shopping Mode:** - Real-time collaboration (one person shops, another adds from home) - Live updates via WebSockets - "John is currently at Costco (aisle 12)" **Shopping Lists:** - Pre-planned lists (weekly meal prep) - Recurring lists (monthly bulk buy) - Shared templates between households --- ## Implementation Timeline ### Sprint 1: Foundation (2-3 weeks) - [ ] Design finalization & review - [ ] Create migration scripts - [ ] Implement new database tables - [ ] Test migration on staging data - [ ] Create new models (household, store, household_list) ### Sprint 2: Backend API (2-3 weeks) - [ ] Implement household management endpoints - [ ] Implement store management endpoints - [ ] Update list endpoints for household+store scope - [ ] Create new middleware (householdAccess, storeAccess) - [ ] Update authentication to remove global role ### Sprint 3: Frontend Core (2-3 weeks) - [ ] **Refactor Context Pattern** (foundational change): - [ ] Refactor AuthContext to internal context + `useAuth()` hook - [ ] Refactor SettingsContext to internal context + `useSettings()` hook - [ ] Update all components using old context pattern - [ ] Create HouseholdContext with `useHousehold()` hook - [ ] Create StoreContext with `useStore()` hook - [ ] Build household switcher UI - [ ] Build store tabs UI - [ ] Update GroceryList page for new API - [ ] Create household management pages ### Sprint 4: Member Management (1-2 weeks) - [ ] Implement invite code system - [ ] Build member management UI - [ ] Implement role updates - [ ] Add join household flow ### Sprint 5: Polish & Testing (1-2 weeks) - [ ] End-to-end testing - [ ] Performance optimization - [ ] Mobile responsiveness - [ ] Documentation updates - [ ] Migration dry-run on production backup ### Sprint 6: Production Migration (1 week) - [ ] Announce maintenance window - [ ] Run migration on production - [ ] Verify data integrity - [ ] Deploy new frontend - [ ] Monitor for issues **Total: 9-14 weeks** --- ## Risk Assessment & Mitigation ### High Risk Areas 1. **Data Loss During Migration** - **Mitigation**: Full backup, dry-run on production copy, rollback plan 2. **Breaking Existing Users** - **Mitigation**: Default household preserves current behavior, phased rollout 3. **Performance Degradation** - **Mitigation**: Proper indexing, query optimization, caching strategy 4. **Complexity Creep** - **Mitigation**: MVP first (basic households), iterate based on feedback ### Testing Strategy 1. **Unit Tests**: All new models and controllers 2. **Integration Tests**: API endpoint flows 3. **Migration Tests**: Verify data integrity post-migration 4. **Load Tests**: Multi-household concurrent access 5. **User Acceptance**: Beta test with small group before full rollout --- ## Open Questions & Decisions Needed ### 1. Item Naming Strategy - **Question**: Should "milk" from Household A and "Milk" from Household B be the same item? - **Options**: - Case-insensitive merge (current behavior, recommended) - Exact match only - User prompt for merge confirmation - **Recommendation**: Case-insensitive with optional household override ### 2. Store Management - **Question**: Should all stores be predefined, or can users create custom stores? - **Options**: - Admin-only store creation (controlled list) - Users can create custom stores (flexible but messy) - Hybrid: predefined + custom - **Recommendation**: Start with predefined stores, add custom later ### 3. Historical Data - **Question**: When a user leaves a household, what happens to their history? - **Options**: - Keep history, anonymize user - Keep history with user name (allows recovery if re-added) - Delete history - **Recommendation**: Keep history with actual user name preserved - **Rationale**: If user is accidentally removed, their contributions remain attributed correctly when re-added - History queries should JOIN with users table but handle missing users gracefully - Display format: Show user name if still exists, otherwise show "User [id]" or handle as deleted account ### 4. Invite System - **Question**: Should invite codes expire? - **Options**: - Never expire (simpler) - 7-day expiration (more secure) - Configurable per household - **Recommendation**: Optional expiration, default to never ### 5. Default Household - **Question**: When user logs in, which household/store do they see? - **Options**: - Last used (remember preference) - Most recently modified list - User-configured default - **Recommendation**: Remember last used in localStorage --- ## Summary & Next Steps ### Recommended Approach: **Hybrid Multi-Tenant Architecture** **Core Principles:** 1. ✅ Shared item catalog with household-specific lists 2. ✅ Per-household roles (not global) 3. ✅ Store-specific classifications 4. ✅ Invite-based household joining 5. ✅ Backward-compatible migration ### Immediate Actions 1. **Review & Approve**: Get stakeholder buy-in on this architecture 2. **Validate Assumptions**: Confirm design decisions (item sharing, store management) 3. **Create Detailed Tickets**: Break down sprints into individual tasks 4. **Set Up Staging**: Create test environment with production data copy 5. **Begin Sprint 1**: Start with database design and migration scripts ### Success Metrics - ✅ Zero data loss during migration - ✅ 100% existing users migrated to default household - ✅ Performance within 20% of current (queries < 200ms) - ✅ Users can create households and invite others - ✅ Lists properly isolated between households - ✅ Mobile UI remains responsive --- ## Appendix A: Example User Flows ### Creating a Household 1. User clicks "Create Household" 2. Enters name "Smith Family" 3. System generates invite code "SMITH2026" 4. User is set as "admin" role (creator is always admin) 5. User can share code with family members ### Joining a Household 1. User receives invite code "SMITH2026" 2. Navigates to /join/SMITH2026 3. Sees "Join Smith Family?" 4. Confirms, added as "user" role by default 5. Admin can promote to "admin" role if needed ### Managing Multiple Households 1. User belongs to "Smith Family" and "Work Team" 2. Navbar shows dropdown: [Smith Family ▼] 3. Clicks dropdown, sees both households 4. Switches to "Work Team" 5. List updates to show Work Team's items 6. Store tabs show Work Team's configured stores ### Adding Item to Store 1. User in "Smith Family" household 2. Sees store tabs: [Costco] [Target] 3. Clicks "Costco" tab 4. Adds "Milk" - goes to Costco list 5. Switches to "Target" tab 6. Adds "Bread" - goes to Target list 7. Milk and Bread are separate list entries (same item, different stores) --- ## Appendix B: Database Size Estimates **Current Single List:** - Users: 10 - Items: 200 - History records: 5,000 **After Multi-Household (10 households, 5 stores each):** - Users: 10 - Households: 10 - Household_members: 30 (avg 3 users per household) - Stores: 5 - Household_stores: 50 - Items: 500 (some shared, some unique) - Household_lists: 2,500 (500 items × 5 stores) - History: 25,000 **Storage Impact:** ~5x increase in list records, but items are deduplicated. **Query Performance:** - Without indexes: O(n) → O(10n) = 10x slower - With indexes: O(log n) → O(log 10n) = minimal impact **Conclusion:** With proper indexing, performance should remain acceptable even at 100+ households.