System Overview
System Overview
Section titled “System Overview”Introduction
Section titled “Introduction”Shaker is the mobile application component of Bartendie, a sophisticated home bar management system. Built with React Native and Expo, Shaker provides cocktail enthusiasts with tools to manage their bar inventory, discover recipes, plan events, and streamline their home bartending experience.
Architecture
Section titled “Architecture”High-Level Architecture
Section titled “High-Level Architecture”┌─────────────────────────────────────────────────────────┐│ Shaker Mobile App ││ (React Native + Expo) │├─────────────────────────────────────────────────────────┤│ Presentation Layer ││ - Expo Router Navigation ││ - RetroUI Components (NeoBrutalism Design) ││ - NativeWind Styling │├─────────────────────────────────────────────────────────┤│ Application Layer ││ - Apollo Client (GraphQL) ││ - Local Data Layer (Recipes, Ingredients, Products) ││ - Custom Hooks & Utilities │├─────────────────────────────────────────────────────────┤│ Infrastructure Layer ││ - Clerk Authentication ││ - Phoenix Channels (Real-time) ││ - Expo Modules (Camera, SecureStore, etc.) │└─────────────────────────────────────────────────────────┘ ↕┌─────────────────────────────────────────────────────────┐│ Well Backend ││ (Elixir + Phoenix) ││ - GraphQL API ││ - PostgreSQL Database ││ - Real-time Subscriptions │└─────────────────────────────────────────────────────────┘Technology Stack
Section titled “Technology Stack”Frontend Framework
Section titled “Frontend Framework”- React Native - Cross-platform mobile development
- Expo SDK 54 - Development platform with managed workflow
- TypeScript - Type safety and developer experience
- Expo Router 6 - File-based navigation system
State Management
Section titled “State Management”- Apollo Client 3.13 - GraphQL client with normalized cache
- Local Data Layer - TypeScript-based data structures for offline-first features
- MMKV (
react-native-mmkv) - Fast binary key-value storage for persisting domain data - React Hooks - Component state and side effects
Styling & Design
Section titled “Styling & Design”- NativeWind - Tailwind CSS for React Native
- RetroUI Design System - NeoBrutalism design tokens
- Tailwind Variants - Type-safe component variants
- Design Tokens - Centralized theme configuration
Backend Communication
Section titled “Backend Communication”- GraphQL - Type-safe API queries and mutations
- Phoenix Channels - WebSocket-based real-time updates
- Absinthe Socket - Phoenix channel integration for GraphQL subscriptions
Authentication & Security
Section titled “Authentication & Security”- Clerk - User authentication and session management
- expo-secure-store - Encrypted local storage
- JWT Tokens - Secure API authentication
Native Features
Section titled “Native Features”- expo-camera - Barcode scanning for product UPC lookup
- expo-image - Optimized image loading and caching
- expo-haptics - Tactile feedback
- expo-keep-awake - Prevents screen sleep during bar service and event management
- expo-file-system - File operations for import/export
- expo-print - PDF generation for shopping lists
Domain Model
Section titled “Domain Model”Shaker implements the Bartendie domain model with the following core entities:
Core Entities
Section titled “Core Entities”Recipe
Section titled “Recipe”Represents a cocktail recipe with ingredients, instructions, and metadata.
Key Attributes:
- Name, description, category, difficulty
- Ingredients (with quantities and units)
- Step-by-step instructions
- Glass type, ice type, garnishes
- Flavor profile (calculated from ingredients)
- Tags, ratings, images
Ingredient
Section titled “Ingredient”Abstract concept of a bar ingredient (e.g., “Vodka”, “Lime Juice”).
Key Attributes:
- Name, type (SPIRIT, LIQUEUR, JUICE, etc.)
- Category (e.g., “Jamaican Rum”, “Citrus Juice”)
- Flavor profile (8-dimensional vector)
- Substitute ingredients (bidirectional relationships)
Flavor Profile Dimensions:
- Sweet, Sour, Bitter, Umami
- Spicy, Herbal, Floral, Fruity
- Each dimension: 0-10 scale
Product
Section titled “Product”Physical item in inventory (e.g., “Tito’s Vodka 750ml”).
Key Attributes:
- Name, brand, size, price
- UPC/barcode for scanning
- Current level (percentage remaining)
- Location in bar
- Purchase history and price tracking
- Maps to one or more Ingredients
Inventory Item
Section titled “Inventory Item”Tracks stock of products, equipment, glassware, and tools.
Types:
- Products (spirits, mixers, etc.)
- Equipment (shakers, strainers, etc.)
- Glassware (coupe, rocks, highball, etc.)
- Tools (jiggers, muddlers, etc.)
Occasion with theme, guest list, and custom menu.
Key Attributes:
- Name, date, theme, venue
- Guest list with RSVPs
- Custom menu (featured and standard drinks)
- Bar setup configuration
- Shopping list (auto-generated)
Shopping List
Section titled “Shopping List”Procurement list for ingredients and supplies.
Features:
- Auto-generation from recipes and events
- Consolidation of duplicate ingredients
- Quantity calculation based on guest count
- Purchase tracking and inventory updates
Design Principles
Section titled “Design Principles”Domain-Driven Design
Section titled “Domain-Driven Design”Shaker follows DDD principles to maintain alignment with the Bartendie domain:
- Ubiquitous Language - Consistent terminology (Recipe, Ingredient, Product, Event)
- Bounded Contexts - Clear separation between Recipe, Inventory, and Event contexts
- Value Objects - Immutable objects like FlavorProfile, RecipeIngredient
- Aggregates - Recipe with Ingredients, Event with Menu and Guests
Offline-First Architecture
Section titled “Offline-First Architecture”- Local data layer for recipes and reference data
- Apollo Client cache for GraphQL data
- Optimistic UI updates
- Background sync when online
Mobile-First UX
Section titled “Mobile-First UX”- Touch-optimized interactions (44pt minimum touch targets)
- Haptic feedback for important actions
- Loading states and skeleton screens
- Error boundaries and graceful degradation
- Platform-specific adaptations (iOS/Android)
NeoBrutalism Design (RetroUI)
Section titled “NeoBrutalism Design (RetroUI)”- Bold, high-contrast colors
- Thick black borders (3-6px)
- Solid shadows (no blur)
- Playful, energetic aesthetic
- Accessibility-first (WCAG AA compliance)
Data Flow
Section titled “Data Flow”GraphQL Data Flow
Section titled “GraphQL Data Flow”User Action → Component ↓Apollo Client (useQuery/useMutation) ↓GraphQL Request → Well Backend ↓Response → Apollo Cache (normalized) ↓Component Re-render (reactive)Local Data Flow
Section titled “Local Data Flow”User Action → Component ↓Local Data Layer (TypeScript) ↓Component State Update ↓Component Re-renderReal-time Updates
Section titled “Real-time Updates”Backend Event → Phoenix Channel ↓Absinthe Socket Subscription ↓Apollo Cache Update ↓Component Re-render (reactive)Local Storage Layer
Section titled “Local Storage Layer”Shaker persists domain data on-device using MMKV for fast binary storage. This enables data to survive app restarts without requiring a network call.
Architecture
Section titled “Architecture”infra/├── localStorage.ts # MMKV wrapper (getItem / setItem)├── hydrateLocalStorage.ts # Startup hydration – loads all domain data└── migrateAsyncStorageToMMKV.ts # One-time migration from legacy AsyncStorageStorage Keys
Section titled “Storage Keys”All keys are namespaced under @bartendie/:
| Key | Data |
|---|---|
inventory_products | Products, instances, quantity tracking |
venues | Venue list |
inventory_tools | Bar tools |
inventory_glassware | Glassware collection |
inventory_equipment | Bar equipment |
events | Events |
menus | Event menus |
budgets | Budget configurations |
shopping_list_items | Shopping list items |
bar_setups | Bar setup configurations per event |
ingredients | Ingredient catalog |
user_recipes | User-created recipes |
Startup Hydration
Section titled “Startup Hydration”Called from app/_layout.tsx after fonts load:
import { hydrateFromLocalStorage } from '@/infra/hydrateLocalStorage';
// For each domain entity:// 1. Try to load from MMKV// 2. If found, hydrate in-memory data store// 3. If missing, seed MMKV from default mock data for next launchawait hydrateFromLocalStorage();AsyncStorage Migration
Section titled “AsyncStorage Migration”On first launch after upgrade, a one-time migration copies all data from AsyncStorage (the previous storage backend) to MMKV:
import { migrateAsyncStorageToMMKVIfNeeded } from '@/infra/migrateAsyncStorageToMMKV';
// Reads each STORAGE_KEY from AsyncStorage, writes to MMKV, sets migration flagawait migrateAsyncStorageToMMKVIfNeeded();The migration flag @bartendie/_migrated_from_async prevents it from running more than once.
Last Modified
Section titled “Last Modified”2026-03-06