Skip to content

Domain Model

The Bartendie domain model outlines the core concepts, entities, and relationships within the home bar organization system. It provides a comprehensive view of the system’s structure using UML class diagrams.

A domain model is a conceptual model of a system that describes:

  • Entities: Objects with identity that persist over time
  • Value Objects: Immutable objects defined by their attributes
  • Aggregates: Clusters of entities and value objects treated as a unit
  • Relationships: How different objects relate to each other
  • Methods: Behaviors associated with the objects

Bartendie is structured around several key domains:

  • Event Planning: Creating and managing events, guest lists, and themes
  • Menu Management: Designing balanced menus for events
  • Recipe Management: Storing and organizing cocktail recipes
  • Inventory Management: Tracking bar stocks and generating shopping lists
  • Bar Setup: Managing tools, equipment, and glassware for events

The diagram below shows the full domain model for the Bartendie system:

classDiagram
    %% Event Aggregate
    class Event {
        <<Aggregate Root>>
        +EventId UUID
        +Name String
        +Date DateTime
        +Description String
        +ExpectedGuestCount Integer
        +Status EventStatus
        +ScheduleEvent()
        +ChangeDate()
        +UpdateGuestCount(count)
        +Cancel()
    }

    class Theme {
        <<Value Object>>
        +Name String
        +ColorScheme String
        +Style String
        +Description String
        +SuggestedIngredients List~String~
    }

    class GuestList {
        +List~Guest~ Guests
        +AddGuest()
        +RemoveGuest()
        +GetPreferences()
        +TrackRSVP()
        +GenerateAttendanceEstimate()
    }

    class Guest {
        +GuestId UUID
        +Name String
        +PreferredSpirits List~String~
        +DislikedIngredients List~String~
        +FavoriteCocktails List~String~
        +Allergies List~String~
        +RSVPStatus RSVPStatus
        +FlavorPreferences List~FlavorProfile~
    }

    class AttendanceEstimate {
        <<Value Object>>
        +ConfirmedCount Integer
        +EstimatedTotal Integer
        +ConfidenceLevel Float
        +UpdateEstimate()
        +AdjustForNoShows(percentage)
    }
    
    %% Venue Aggregate
    class Venue {
        <<Aggregate Root>>
        +VenueId UUID
        +Name String
        +Address String
        +CreateVenue()
        +UpdateDetails()
        +AddStorage()
        +AddBar(bar)
        +RemoveBar(barId)
    }

    class Storage {
        +StorageId UUID
        +Name String
        +Type String
    }

    class Bar {
        +BarId UUID
        +Name String
        +Description String
        +VenueId UUID
        +Features List~String~
        +CreateBar()
        +UpdateBar()
    }
    
    %% Menu Aggregate
    class Menu {
        <<Aggregate Root>>
        +MenuId UUID
        +Name String
        +Description String
        +EventId UUID
        +IsFinal Boolean
        +OverallFlavorProfile FlavorProfile
        +CreateMenu()
        +AddDrink()
        +RemoveDrink()
        +FinalizeMenu()
        +CalculateIngredientNeeds()
        +AnalyzeMenuBalance()
    }

    class DrinkItem {
        +DrinkId UUID
        +Name String
        +Description String
        +Category DrinkCategory
        +IsFeatured Boolean
        +Image URL
        +Feature()
        +Unfeature()
        +UpdateRecipe()
    }

    class Recipe {
        <<Aggregate Root>>
        +RecipeId UUID
        +Name String
        +Description String
        +Author String
        +CreationDate DateTime
        +Instructions String
        +PrepTime TimeSpan
        +GlassType GlassType
        +IceType IceType
        +GarnishInstructions String
        +Rating Float
        +RecipeBalance RecipeBalance
        +RequiredTools List~RecipeTool~
        +Tags List~Tag~
        +CreateRecipe()
        +UpdateRecipe()
        +RateRecipe()
        +TagRecipe()
    }

    class RecipeIngredient {
        +IngredientId UUID
        +Quantity Decimal
        +Unit MeasurementUnit
        +IsOptional Boolean
        +Notes String
        +UpdateQuantity()
    }

    class Ingredient {
        <<Aggregate Root>>
        +IngredientId UUID
        +Name String
        +Type IngredientType
        +Category String
        +FlavorProfile IngredientProfile
        +Substitutes List~Ingredient~
        +CreateIngredient()
        +UpdateIngredient()
        +AddSubstitute()
    }

    class IngredientProfile {
        <<Value Object>>
        +Sweet Integer
        +Sour Integer
        +Bitter Integer
        +Umami Integer
        +Spicy Integer
        +Herbal Integer
        +Floral Integer
        +Fruity Integer
    }

    class RecipeBalance {
        <<Value Object>>
        +Sweet Integer
        +Sour Integer
        +Bitter Integer
        +Umami Integer
        +Spicy Integer
        +Herbal Integer
        +Floral Integer
        +Fruity Integer
        +CalculateBalance()
    }

    class RecipeTool {
        +ToolId UUID
        +Name String
        +IsRequired Boolean
    }

    class BatchRecipe {
        +BatchRecipeId UUID
        +RecipeId UUID
        +Servings Integer
        +ScalingFactor Decimal
        +TotalVolume Decimal
        +CalculateScaledQuantities()
    }

    %% Inventory Aggregate
    class Inventory {
        <<Aggregate Root>>
        +InventoryId UUID
        +CheckStock(List~IngredientId~)
        +GenerateShoppingList(Menu)
        +UpdateStock(List~IngredientQuantity~)
        +TrackTools()
        +TrackGlassware()
        +TrackEquipment()
        +ScanProduct(barcode)
        +LookupProductData(barcode)
        +AlertLowStock()
    }

    class Product {
        <<Aggregate Root>>
        +ProductId UUID
        +Name String
        +Brand String
        +Type ProductType
        +Category String
        +Volume Decimal
        +VolumeUnit MeasurementUnit
        +AlcoholContent Float
        +TrackingMode TrackingMode
        +DefaultPrice Decimal
        +PriceHistory List~PriceHistoryEntry~
        +Barcode ProductBarcode
        +ProductData ProductData
        +ImageUrl String
        +Tags List~Tag~
        +CreateProduct()
        +UpdateProduct()
        +GetTotalQuantity()
        +GetAvailableAmount()
        +AddToShoppingList()
        +ScanBarcode()
        +LookupProductData(barcode)
        +RecordPrice(price, source, vendor)
    }

    class TrackingMode {
        <<Enumeration>>
        LEVEL_TRACKED
        QUANTITY_ONLY
    }

    class ProductInstance {
        +InstanceId UUID
        +ProductId UUID
        +Status InstanceStatus
        +CurrentLevel ProductLevel
        +StorageLocation String
        +OpenedDate DateTime
        +ExpirationDate DateTime
        +PurchasePrice Decimal
        +Notes String
        +Open()
        +UpdateLevel(amount)
        +MarkEmpty()
        +MarkDepleted()
    }

    class InstanceStatus {
        <<Enumeration>>
        UNOPENED
        IN_USE
        EMPTY
        EXPIRED
    }

    class QuantityTracking {
        +TrackingId UUID
        +ProductId UUID
        +TotalQuantity Integer
        +Unit MeasurementUnit
        +StorageLocation String
        +LastRestocked DateTime
        +MinimumStock Integer
        +AddQuantity(amount)
        +RemoveQuantity(amount)
        +SetQuantity(amount)
        +CheckLowStock()
    }

    class ShoppingList {
        <<Aggregate Root>>
        +ShoppingListId UUID
        +EventId UUID
        +MenuId UUID
        +Status ShoppingListStatus
        +GeneratedDate DateTime
        +TotalEstimatedCost Decimal
        +Items List~ShoppingListItem~
        +GenerateFromMenu()
        +AddItem()
        +RemoveItem()
        +CheckOffItem()
        +CalculateTotalCost()
    }

    class ShoppingListItem {
        +ItemId UUID
        +IngredientId UUID
        +ProductId UUID
        +Name String
        +Quantity Decimal
        +Unit MeasurementUnit
        +EstimatedCost Decimal
        +IsChecked Boolean
        +Category String
        +CheckOff()
        +UpdateQuantity()
    }

    class ProductBarcode {
        <<Value Object>>
        +Code String
        +Type BarcodeType
        +ScannedDate DateTime
    }

    class ProductData {
        <<Value Object>>
        +Name String
        +Brand String
        +Category String
        +Volume String
        +Description String
        +ImageUrl String
        +Ingredients String
        +AlcoholPercentage Float
        +Manufacturer String
        +Country String
        +Source ProductSource
    }

    class PriceHistoryEntry {
        <<Value Object>>
        +Price Decimal
        +Date DateTime
        +Source PriceSource
        +Vendor String
        +Notes String
    }

    class ProductSource {
        <<Enumeration>>
        OPENFOODFACTS
        UPCITEMDB
        BARCODEDLOOKUP
        MANUAL
    }

    class PriceSource {
        <<Enumeration>>
        PURCHASE
        MANUAL
        SHOPPING_LIST
        IMPORT
    }

    class ProductLevel {
        <<Value Object>>
        +CurrentAmount Decimal
        +TotalCapacity Decimal
        +Percentage Float
        +Status LevelStatus
    }

    %% Bar Setup Aggregate
    class BarSetup {
        <<Aggregate Root>>
        +SetupId UUID
        +BarId UUID
        +EventId UUID
        +Layout String
        +RequiredTools List~BarTool~
        +RequiredEquipment List~Equipment~
        +RequiredGlassware List~Glassware~
        +SetupInstructions String
        +Tags List~Tag~
        +CreateSetup()
        +AdjustLayout()
        +FinalizeSetup()
        +VerifyToolAvailability()
    }

    class BarTool {
        +ToolId UUID
        +Name String
        +Type ToolType
        +Quantity Integer
        +Condition String
        +IsAvailable Boolean
        +AddTool()
        +UpdateCondition()
    }

    class Equipment {
        +EquipmentId UUID
        +Name String
        +Type String
        +Quantity Integer
        +Condition String
        +IsAvailable Boolean
        +AddEquipment()
        +UpdateCondition()
    }

    class Glassware {
        +GlasswareId UUID
        +Type GlassType
        +Quantity Integer
        +Condition String
        +IsAvailable Boolean
        +AddGlassware()
        +UpdateQuantity()
    }

    %% Batched Cocktail Aggregate
    class BatchedCocktail {
        <<Aggregate Root>>
        +BatchId UUID
        +EventId UUID
        +RecipeId UUID
        +BatchRecipe BatchRecipe
        +Servings Integer
        +PreparedDate DateTime
        +ExpirationDate DateTime
        +StorageLocation String
        +Status BatchStatus
        +CreateBatch()
        +MarkAsServed()
        +UpdateServings()
    }

    class BatchIngredient {
        <<Value Object>>
        +IngredientId UUID
        +ScaledQuantity Decimal
        +Unit MeasurementUnit
        +CalculateScaled()
    }

    %% Flavor Profile System
    class FlavorProfile {
        +ProfileId UUID
        +Name String
        +Description String
        +Characteristics List~FlavorCharacteristic~
        +DefineProfile()
        +UpdateProfile()
    }

    class FlavorCharacteristic {
        <<Value Object>>
        +Name String
        +Intensity Integer
        +Category String
    }

    %% Tagging System
    class Taggable {
        <<Interface>>
        +ItemId UUID
        +Tags List~Tag~
        +AddTag(tag)
        +RemoveTag(tag)
        +GetTagsByType(type)
    }

    class Tag {
        +TagId UUID
        +Name String
        +Type TagType
        +Color String
        +CreateTag()
    }

    %% Relationships
    Event "1" *-- "1" Theme
    Event "1" *-- "1" GuestList
    Event "1" *-- "1" AttendanceEstimate
    GuestList "1" *-- "*" Guest
    Event "1" *-- "1" Venue
    Event "1" *-- "1..*" Menu
    Event "1" *-- "0..1" ShoppingList
    Menu "1" *-- "*" DrinkItem
    DrinkItem "1" *-- "1" Recipe
    Recipe "1" *-- "*" RecipeIngredient
    RecipeIngredient "*" -- "1" Ingredient
    Recipe "1" -- "1" RecipeBalance
    Recipe "*" -- "*" RecipeTool
    Ingredient "1" -- "1" IngredientProfile
    Ingredient "*" -- "*" Product
    Event "1" *-- "1" BarSetup
    Event "1" *-- "*" BatchedCocktail
    BatchedCocktail "1" *-- "1" BatchRecipe
    BatchedCocktail "1" *-- "*" BatchIngredient
    BatchRecipe "1" -- "1" Recipe
    Menu "1" -- "1" FlavorProfile
    FlavorProfile "1" *-- "*" FlavorCharacteristic
    Venue "1" *-- "*" Storage
    Venue "1" *-- "*" Bar
    BarSetup "1" -- "1" Bar
    BarSetup "*" -- "*" BarTool
    BarSetup "*" -- "*" Equipment
    BarSetup "*" -- "*" Glassware
    ShoppingList "1" *-- "*" ShoppingListItem
    ShoppingListItem "*" -- "0..1" Ingredient
    ShoppingListItem "*" -- "0..1" Product
    Product "1" -- "1" ProductBarcode
    Product "1" -- "1" ProductData
    Product "1" -- "1" TrackingMode
    Product "1" -- "*" ProductInstance : level-tracked >
    Product "1" -- "0..1" QuantityTracking : quantity-only >
    ProductInstance "1" -- "1" InstanceStatus
    ProductInstance "1" -- "1" ProductLevel
    Recipe --|> Taggable
    Product --|> Taggable
    Event --|> Taggable
    BarSetup --|> Taggable
    Taggable "*" -- "*" Tag

    %% User Management Aggregate
    class User {
        <<Aggregate Root>>
        +UserId UUID
        +Username String
        +Email String
        +PasswordHash String
        +Profile UserProfile
        +Roles List~Role~
        +IsActive Boolean
        +CreatedAt DateTime
        +LastLoginAt DateTime
        +CreateUser()
        +UpdateProfile()
        +ChangePassword()
        +AssignRole()
        +Activate()
        +Deactivate()
    }

    class UserProfile {
        <<Value Object>>
        +FirstName String
        +LastName String
        +DisplayName String
        +Avatar String
        +Preferences UserPreferences
        +UpdateProfile()
    }

    class Role {
        +RoleId UUID
        +Name String
        +Description String
        +Permissions List~Permission~
        +CreateRole()
        +UpdateRole()
        +AddPermission()
        +RemovePermission()
    }

    class Permission {
        <<Value Object>>
        +Name String
        +Resource String
        +Action String
        +Description String
    }

    %% Notification Aggregate
    class Notification {
        <<Aggregate Root>>
        +NotificationId UUID
        +UserId UUID
        +Type NotificationType
        +Title String
        +Message String
        +IsRead Boolean
        +CreatedAt DateTime
        +ReadAt DateTime
        +ExpiresAt DateTime
        +CreateNotification()
        +MarkAsRead()
        +Archive()
    }

    %% Audit Aggregate
    class AuditLog {
        <<Aggregate Root>>
        +LogId UUID
        +UserId UUID
        +EntityType String
        +EntityId UUID
        +Action String
        +OldValues String
        +NewValues String
        +Timestamp DateTime
        +IPAddress String
        +UserAgent String
        +LogAction()
        +GetHistory()
    }

    %% Additional Relationships
    User "1" *-- "1" UserProfile
    User "*" -- "*" Role
    Role "1" *-- "*" Permission
    User "1" -- "*" Notification
    User "1" -- "*" AuditLog

The Bartendie system is organized around several key aggregates:

The Event Aggregate represents a planned cocktail event, including:

  • Event: The main entity with date, name, and description
  • Theme: Value object defining the event’s stylistic concept
  • GuestList: Collection of guests with preferences and RSVP status
  • AttendanceEstimate: Projection of expected attendance

Events are the central organizing concept in Bartendie, providing context for menus, guest preferences, and bar setup.

The Venue Aggregate represents physical locations where events take place.

  • Venue: The root entity for a physical location, including its address and available facilities. The venue manages multiple bars and storage areas within its boundaries.
  • Storage: A designated area within a venue for storing inventory (e.g., dry storage, cold storage).
  • Bar: A specific bar area within a venue that can be set up for an event. Each bar has a unique identity, name, description, and features that define its capabilities.

The Bar entity represents a physical bar location within a venue and includes:

Attributes:

  • BarId: Unique identifier for the bar
  • Name: Descriptive name (e.g., “Main Bar”, “Kitchen Counter Bar”)
  • Description: Optional detailed description of the bar’s characteristics
  • VenueId: Reference to the parent venue
  • Features: List of bar capabilities (e.g., “Full Service”, “Wine Bar”, “Coffee Station”)

Behaviors:

  • CreateBar(): Initialize a new bar within the venue
  • UpdateBar(): Modify bar details and features

Relationship to Venue: Bars exist within the composition boundary of a Venue. A venue can contain multiple bars, but each bar belongs to exactly one venue. The venue is responsible for managing the lifecycle of its bars through the AddBar() and RemoveBar() methods.

Venues provide the physical context for events and contain the spaces used for storage and service.

The Menu Aggregate represents a curated collection of drinks for an event:

  • Menu: The main entity containing drink items
  • DrinkItem: Individual cocktail offerings on a menu
  • FlavorProfile: The overall flavor balance of the menu

Menus are linked to events and contain references to recipes that will be served.

The Recipe Aggregate defines how to prepare specific cocktails:

  • Recipe: The main entity with ingredients, instructions, and preparation details
  • RecipeIngredient: Specific ingredient with quantity and unit measurements, linking recipes to ingredients
  • Ingredient: Abstract ingredient concept (e.g., “vodka”) that can map to multiple products
  • IngredientProfile: Flavor characteristics of an ingredient (sweet, sour, bitter, etc.)
  • RecipeBalance: Analysis of flavor components calculated from ingredient profiles
  • RecipeTool: Mapping between recipes and required bar tools
  • BatchRecipe: Scaled version of a recipe for multiple servings with adjusted quantities

Recipes are the foundation of drink preparation and are referenced by menu items. The ingredient abstraction allows recipes to specify generic ingredients (e.g., “bourbon”) while the inventory tracks specific products (e.g., “Maker’s Mark Bourbon”).

The Inventory Aggregate tracks all available bar ingredients and tools using two distinct inventory tracking patterns:

  • Inventory: The main entity managing all available items
  • Product: Product type/definition with brand, type, and tracking mode (not a specific bottle)
  • TrackingMode: Enumeration defining how a product is tracked (LEVEL_TRACKED or QUANTITY_ONLY)
  • ProductInstance: Individual bottle/container for level-tracked products (e.g., “Maker’s Mark bottle #1 at 45% full”)
  • InstanceStatus: Status of a product instance (UNOPENED, IN_USE, EMPTY, EXPIRED)
  • QuantityTracking: Simple count-based tracking for quantity-only products (e.g., “24 bottles of Topo Chico”)
  • ProductBarcode: Value object containing barcode information for product lookup
  • ProductData: Value object with product information from external databases
  • ProductLevel: Value object tracking current fill level and status of individual instances
  • Ingredient: Component used in recipes, mapped to products
  • ShoppingList: Needed items based on planned menus and current inventory
  • ShoppingListItem: Individual item on a shopping list with quantity, cost, and check-off status

The inventory system supports two distinct tracking patterns based on product characteristics:

Level-Tracked Products (TrackingMode.LEVEL_TRACKED):

  • Used for premium spirits, liqueurs, and expensive bottles where individual bottle tracking is valuable
  • Each physical bottle is represented by a ProductInstance entity
  • Tracks individual bottle lifecycle: UNOPENED → IN_USE → EMPTY
  • Maintains precise fill levels for each bottle (e.g., “bottle #1 is 45% full, bottle #2 is unopened”)
  • Enables tracking which specific bottle was used when and where it’s stored
  • Total available amount is calculated by summing all non-empty instance volumes

Quantity-Only Products (TrackingMode.QUANTITY_ONLY):

  • Used for mixers, canned beverages, and consumables where items are fungible
  • Single QuantityTracking entity per product maintains a simple count
  • No individual item identity or level tracking needed
  • Efficient for high-volume, low-value items
  • Simple increment/decrement operations for stock updates

The inventory system allows tracking of stock levels and generation of shopping lists based on planned menus. Products can be scanned via barcode to auto-populate product data from external databases. Multiple products can map to a single ingredient (e.g., different bourbon brands all map to “bourbon” ingredient).

The system supports barcode scanning for automatic product identification and data population:

Barcode Scanning Workflow:

  1. User scans product barcode using mobile device or barcode scanner
  2. System extracts barcode code and type (UPC, EAN, QR, CODE128)
  3. System attempts product data lookup from multiple external sources:
    • OpenFoodFacts: Open database of food products
    • UPCitemdb: UPC product database
    • BarcodeLookup: Commercial barcode lookup service
  4. If product data is found, ProductData value object is populated with:
    • Product name, brand, category
    • Volume information
    • Description and ingredients
    • Image URL
    • Alcohol percentage (if applicable)
    • Manufacturer and country information
  5. ProductBarcode value object stores the scanned barcode with timestamp
  6. User can review and edit auto-populated data before creating product
  7. If no data is found, user manually enters product information

Price History Tracking:

Products maintain a price history to track cost changes over time:

  • Each PriceHistoryEntry records price, date, source (purchase, manual, shopping list, import), vendor, and optional notes
  • Default price is typically the most recent purchase price
  • Price history enables cost analysis and budget planning for events
  • Historical price data helps identify trends and optimize purchasing decisions

For detailed information about the inventory tracking patterns, see Inventory Tracking Patterns.

The Bar Setup Aggregate defines the physical arrangement and tools needed for an event:

  • BarSetup: The main entity defining layout and required items for a specific event
  • BarTool: Specific implement for drink preparation (shaker, jigger, strainer, etc.) with quantity and availability tracking
  • Equipment: Larger items required for bar operation (ice maker, blender, etc.) with condition tracking
  • Glassware: Specific vessels for serving drinks (coupe, rocks, highball, etc.) with quantity tracking

Bar setup ensures that all necessary tools and equipment are available for an event. The system can verify tool availability against inventory and generate a checklist of required items based on the event’s menu.

The Batched Cocktail Aggregate manages pre-made cocktails for events:

  • BatchedCocktail: The main entity representing prepared batches with servings, preparation date, and storage location
  • BatchRecipe: Modified recipe for large-scale preparation with scaling factor and total volume calculations
  • BatchIngredient: Scaled ingredient quantities for batch preparation based on the number of servings

Batched cocktails help in efficient service during events by allowing preparation in advance. The system automatically scales ingredient quantities based on the desired number of servings and tracks batch status (prepared, serving, depleted). This is essential for events with 20+ guests where individual cocktail preparation would be impractical.

The User Management Aggregate handles authentication, authorization, and user profiles:

  • User: The main entity representing a system user with authentication credentials and profile information
  • UserProfile: Value object containing personal information and preferences
  • Role: Entity defining user roles with associated permissions
  • Permission: Value object defining specific access rights to resources and actions

This aggregate ensures secure access to the system and proper authorization for different user types (hosts, bartenders, administrators).

The Notification Aggregate manages system communications and alerts:

  • Notification: The main entity representing messages, alerts, and reminders sent to users

This aggregate handles various types of notifications including inventory alerts, event reminders, and system messages.

The Audit Aggregate provides comprehensive tracking and history:

  • AuditLog: The main entity recording all system operations, changes, and user actions

This aggregate ensures accountability, compliance, and provides detailed history for troubleshooting and analysis.

The Flavor Profile System analyzes and categorizes flavors across the system:

  • FlavorProfile: Collection of taste characteristics used to describe menus and overall flavor balance
  • FlavorCharacteristic: Specific taste attribute with name, intensity (0-10), and category
  • IngredientProfile: Flavor properties of specific ingredients, defining their taste characteristics on a 0-10 scale for: sweet, sour, bitter, umami, spicy, herbal, floral, and fruity
  • RecipeBalance: Analysis of a recipe’s flavor components, calculated by aggregating ingredient profiles using the same eight characteristics

Flavor Characteristics (0-10 scale):

  • Sweet: Sugar content and perceived sweetness
  • Sour: Acidity and tartness
  • Bitter: Bitterness from herbs, coffee, or alcohol
  • Umami: Savory, meaty flavors
  • Spicy: Heat and spice intensity
  • Herbal: Botanical and herbal notes
  • Floral: Floral and perfumed aromas
  • Fruity: Fruit flavors and esters

This system helps in creating balanced menus and matching drinks to guest preferences. The system can:

  • Calculate overall menu balance by analyzing all drinks across all eight flavor dimensions
  • Identify gaps in flavor profiles (e.g., “menu is too sweet, needs more bitter drinks”)
  • Match recipes to guest flavor preferences
  • Suggest complementary drinks for menu variety
  • Calculate recipe balance by aggregating ingredient profiles with weighted quantities

The Tagging System provides organization and categorization:

  • Taggable: Interface for entities that can be tagged (implemented by Recipe, Product, Event, BarSetup)
  • Tag: Label applied to various entities for organization with name, type, and color

Many entities implement the Taggable interface, allowing for flexible categorization and search. Tag types include:

  • Occasion: party, wedding, holiday, casual
  • Season: summer, winter, spring, fall
  • Flavor: citrus, tropical, herbal, spicy
  • Technique: shaken, stirred, built, blended
  • Difficulty: easy, intermediate, advanced

The model uses several value objects to represent immutable concepts:

  • Theme: Defines the stylistic concept for an event with name, color scheme, style, and suggested ingredients
  • AttendanceEstimate: Projection of expected attendance with confirmed count, estimated total, and confidence level
  • UserProfile: User personal information with first name, last name, display name, avatar, and preferences
  • Permission: Access rights with resource, action, and description
  • RecipeBalance: Analysis of a recipe’s flavor components (sweet, sour, bitter, umami, spicy, herbal, floral, fruity)
  • IngredientProfile: Flavor characteristics of an ingredient (sweet, sour, bitter, umami, spicy, herbal, floral, fruity) on a 0-10 scale
  • ProductBarcode: Identification code for a product with code, barcode type, and scanned date timestamp
  • ProductData: Descriptive information about a product from external databases (name, brand, category, volume, description, image URL, ingredients, alcohol percentage, manufacturer, country, source)
  • ProductLevel: Current amount and status of a product with percentage and level status
  • PriceHistoryEntry: Historical price record with price, date, source, vendor, and notes
  • BatchIngredient: Scaled ingredient for batch preparation with calculated quantities
  • FlavorCharacteristic: Specific taste attribute with name, intensity (0-10), and category

The model includes several enumerations to represent fixed sets of values:

  • EventStatus: Status of an event (planned, in_progress, completed, cancelled)
  • RSVPStatus: Status of a guest’s RSVP (invited, confirmed, declined, maybe, no_response)
  • DrinkCategory: Category of a drink (signature, classic, mocktail, punch, shot, etc.)
  • IngredientType: Type of ingredient (spirit, mixer, modifier, sweetener, garnish, bitters, other)
  • ProductType: Type of product (spirit, wine, beer, mixer, garnish, tool, equipment, glassware, other)
  • TrackingMode: Inventory tracking mode for products (LEVEL_TRACKED for individual bottle tracking, QUANTITY_ONLY for simple count-based tracking)
  • InstanceStatus: Status of a product instance (UNOPENED, IN_USE, EMPTY, EXPIRED)
  • MeasurementUnit: Unit of measurement (oz, ml, cl, l, dash, tsp, tbsp, cup, count, etc.)
  • GlassType: Type of glass (coupe, rocks, highball, collins, flute, martini, wine, shot, etc.)
  • ToolType: Type of bar tool (shaker, strainer, jigger, spoon, muddler, peeler, knife, etc.)
  • IceType: Type of ice (cube, sphere, crushed, cracked, block, none, etc.)
  • ShoppingListStatus: Status of a shopping list (draft, final, in_progress, completed)
  • TagType: Type of tag (occasion, season, flavor, technique, difficulty, style, etc.)
  • NotificationType: Type of notification (info, warning, error, success, reminder, alert, system)
  • BatchStatus: Status of a batched cocktail (preparing, ready, serving, depleted)
  • LevelStatus: Status of product level (full, high, medium, low, empty)
  • BarcodeType: Type of barcode (UPC, EAN, QR, CODE128)
  • ProductSource: Source of product data (OPENFOODFACTS, UPCITEMDB, BARCODEDLOOKUP, MANUAL)
  • PriceSource: Source of price data (PURCHASE, MANUAL, SHOPPING_LIST, IMPORT)

The model interacts with several external systems:

  • OpenFoodFacts: Open database of food products providing product information via barcode lookup
  • UPCitemdb: UPC product database for product data retrieval
  • BarcodeLookup: Commercial barcode lookup service for comprehensive product information
  • BarcodeScanner: External system (mobile device or hardware scanner) for scanning product barcodes

These external systems are used during the barcode scanning workflow to automatically populate ProductData when a product is scanned. The system attempts lookup from multiple sources in order of preference, falling back to manual entry if no data is found.

The domain model provides a blueprint for implementation using domain-driven design principles:

  • Entities will be implemented as classes with identity and persistence
  • Value Objects will be implemented as immutable classes
  • Aggregates will enforce consistency boundaries and rules
  • Repositories will provide data access for aggregates
  • Services will implement operations that don’t naturally belong to entities

Aggregate Boundaries:

  • Each aggregate root controls access to its internal entities
  • External references use IDs, not direct object references
  • Transactions should not span multiple aggregates

Value Object Immutability:

  • All value objects are immutable after creation
  • Changes create new instances rather than modifying existing ones
  • Equality is based on attribute values, not identity

Repository Patterns:

  • One repository per aggregate root
  • Repositories return fully-formed aggregates
  • Queries should be optimized for common access patterns

Domain Services:

  • Used for operations that don’t naturally fit within an entity
  • Examples: ShoppingListGenerator, MenuBalancer, BatchRecipeScaler
  • Services coordinate between multiple aggregates

This comprehensive domain model serves as the foundation for the Bartendie system, ensuring that the software accurately reflects the needs and workflows of home bartenders planning and executing cocktail events.

For a comprehensive glossary of terms used throughout the Bartendie system, refer to the Glossary document.