28 KiB
Multi-Household & Multi-Store Architecture Plan
Executive Summary
This document outlines the architecture and implementation strategy for extending the application to support:
- Multiple Households - Users can belong to multiple households (families, roommates, etc.)
- Multiple Stores - Households can manage lists for different store types (Costco, Target, Walmart, etc.)
Current Architecture Analysis
Existing Schema
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
itemstable (id, name, default_image, created_at) - Household-specific
household_listtable references item + household - Each household can override classifications per store
- Global
Proposed Schema Design
New Tables
-- 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
-- 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
// 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:
- Create default household "Main Household"
- Migrate all existing users → household_members (old admins become household admins, others become users)
- Keep existing
users.rolecolumn, update values:admin→system_admin(app-wide admin)editor→user(standard user)viewer→user(standard user)
- Migrate grocery_list → household_lists (all to default household + default store)
- 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:
req.user = { id, username, role }
After:
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 (
<AuthContext.Provider value={{ user, setUser }}>
{children}
</AuthContext.Provider>
);
}
// 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)
// 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 (
<AuthContext.Provider value={{ user, token, login, logout }}>
{children}
</AuthContext.Provider>
);
}
// 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
- Encapsulation - Context implementation is hidden, only the hook is public API
- Type Safety - Can add TypeScript types to the hook return value
- Validation - Hook can check if used within provider (prevents null errors)
- Cleaner Imports - One import instead of two (
useContext+Context) - Easier Refactoring - Can change context internals without affecting consumers
- 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:
- Keep old context export temporarily
- Add custom hook export
- Update all components to use hook
- Remove old context export
- Make context
constinternal to file
Frontend Architecture Changes
Context Structure
// 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)</option>
<option value={2}>Work Team (Editor)</option>
<option value={3}>Apartment 5B (Viewer)</option>
<option>+ Create New Household</option>
{user.systemRole === 'system_admin' && (
<option>⚙️ System Admin</option>
)}
</HouseholdDropdown>
Store Tabs (Within Household)
<StoreTabs householdId={activeHousehold.id}>
<Tab active>Costco</Tab>
<Tab>Target</Tab>
<Tab>Walmart</Tab>
{(isAdmin || isOwner) && <Tab>+ Add Store</Tab>} → User settings (personal)
UI Components
Household Switcher (Navbar)
<HouseholdDropdown>
<option value={1}>Smith Family</option>
<option value={2}>Work Team</option>
<option value={3}>Apartment 5B</option>
<option>+ Create New Household</option>
</HouseholdDropdown>
Store Tabs (Within Household)
<StoreTabs householdId={activeHousehold.id}>
<Tab active>Costco</Tab>
<Tab>Target</Tab>
<Tab>Walmart</Tab>
<Tab>+ Add Store</Tab>
</StoreTabs>
Migration Strategy
Phase 1: Database Schema (Breaking Change)
Step 1: Backup
pg_dump grocery_list > backup_$(date +%Y%m%d).sql
Step 2: Run Migrations
-- 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)
- ✅ Create new models (households, stores, household_lists)
- ✅ Create new middleware (householdAccess, storeAccess)
- ✅ Create new controllers (households, stores)
- ✅ Add new routes alongside old ones
- ✅ Update list controllers to be household+store aware
- ✅ Deprecate old routes (return 410 Gone)
Phase 3: Frontend UI (Incremental)
- ✅ Refactor Context Pattern (applies to all contexts)
- Move
createContextinside component files (not exported) - Export custom hooks instead:
useAuth(),useHousehold(),useStore(),useSettings() - Consumers use hooks directly instead of
useContext(ExportedContext)
- Move
- ✅ Create HouseholdContext with
useHousehold()hook - ✅ Create StoreContext with
useStore()hook - ✅ Refactor existing AuthContext to use custom
useAuth()hook - ✅ Refactor existing SettingsContext to use custom
useSettings()hook - ✅ Add household switcher to navbar
- ✅ Create household management pages
- ✅ Add store tabs to list view
- ✅ Update all API calls to use household + store IDs
- ✅ Add invite system UI
- ✅ 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
- Refactor AuthContext to internal context +
- 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
-
Data Loss During Migration
- Mitigation: Full backup, dry-run on production copy, rollback plan
-
Breaking Existing Users
- Mitigation: Default household preserves current behavior, phased rollout
-
Performance Degradation
- Mitigation: Proper indexing, query optimization, caching strategy
-
Complexity Creep
- Mitigation: MVP first (basic households), iterate based on feedback
Testing Strategy
- Unit Tests: All new models and controllers
- Integration Tests: API endpoint flows
- Migration Tests: Verify data integrity post-migration
- Load Tests: Multi-household concurrent access
- 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:
- ✅ Shared item catalog with household-specific lists
- ✅ Per-household roles (not global)
- ✅ Store-specific classifications
- ✅ Invite-based household joining
- ✅ Backward-compatible migration
Immediate Actions
- Review & Approve: Get stakeholder buy-in on this architecture
- Validate Assumptions: Confirm design decisions (item sharing, store management)
- Create Detailed Tickets: Break down sprints into individual tasks
- Set Up Staging: Create test environment with production data copy
- 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
- User clicks "Create Household"
- Enters name "Smith Family"
- System generates invite code "SMITH2026"
- User is set as "admin" role (creator is always admin)
- User can share code with family members
Joining a Household
- User receives invite code "SMITH2026"
- Navigates to /join/SMITH2026
- Sees "Join Smith Family?"
- Confirms, added as "user" role by default
- Admin can promote to "admin" role if needed
Managing Multiple Households
- User belongs to "Smith Family" and "Work Team"
- Navbar shows dropdown: [Smith Family ▼]
- Clicks dropdown, sees both households
- Switches to "Work Team"
- List updates to show Work Team's items
- Store tabs show Work Team's configured stores
Adding Item to Store
- User in "Smith Family" household
- Sees store tabs: [Costco] [Target]
- Clicks "Costco" tab
- Adds "Milk" - goes to Costco list
- Switches to "Target" tab
- Adds "Bread" - goes to Target list
- 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.