Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture Overview


System Overview

The Pricing Engine is a high-performance, API-only Rails 8 application for dynamic pricing rule management. It provides Partner API for CRUD operations and Internal API for price calculations.


Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                              CLIENTS                                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                          │
│  │Partner Portal│  │Internal Svc │  │ Admin Tools │                          │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘                          │
└─────────┼────────────────┼────────────────┼─────────────────────────────────┘
          │                │                │
          └────────────────┴────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         RAILS 8 API APPLICATION                              │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                         RACK MIDDLEWARE                               │   │
│  │  Rack::Attack (Rate Limit) │ Rack::CORS │ Lograge (Logging)          │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                       API CONTROLLERS                                 │   │
│  │  ┌────────────────────────┐  ┌────────────────────────────────────┐  │   │
│  │  │   Partner API (v1)     │  │         Internal API (v1)          │  │   │
│  │  │  • PricingRulesCtrl    │  │  • PricesController (calculate)    │  │   │
│  │  │    - CRUD operations   │  │  • BulkCalculateController         │  │   │
│  │  │    - Clone / Toggle    │  │  • SimulateController              │  │   │
│  │  └────────────────────────┘  └────────────────────────────────────┘  │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                       SERVICE LAYER                                   │   │
│  │  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────────┐   │   │
│  │  │ Pricing::Engine │  │ RuleMatcher     │  │ ConditionEvaluator  │   │   │
│  │  │ (Orchestrator)  │  │ (Find Rules)    │  │ (Match Conditions)  │   │   │
│  │  └─────────────────┘  └─────────────────┘  └─────────────────────┘   │   │
│  │  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────────┐   │   │
│  │  │ PriceCalculator │  │ PriorityResolver│  │GlobalLimitsEnforcer │   │   │
│  │  │ (Apply Values)  │  │ (Sort/Filter)   │  │ (Cap Adjustments)   │   │   │
│  │  └─────────────────┘  └─────────────────┘  └─────────────────────┘   │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                       DATA LAYER                                      │   │
│  │  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────────┐   │   │
│  │  │  PricingRule    │  │DateTimeCondition│  │   ConditionItem     │   │   │
│  │  │  (Parent)       │  │  (Condition)    │  │   (Time Slots)      │   │   │
│  │  └─────────────────┘  └─────────────────┘  └─────────────────────┘   │   │
│  │  ┌─────────────────┐  ┌─────────────────┐                            │   │
│  │  │PricingRules     │  │   Serializers   │                            │   │
│  │  │Packages (Join)  │  │   (JSON output) │                            │   │
│  │  └─────────────────┘  └─────────────────┘                            │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
                        ┌─────────────────────┐
                        │     POSTGRESQL      │
                        │  • pricing_rules    │
                        │  • date_time_conds  │
                        │  • condition_items  │
                        │  • pricing_packages │
                        └─────────────────────┘

Technology Stack

LayerTechnologyVersionPurpose
RuntimeRuby + YJIT3.3.6High-performance execution
FrameworkRails (API mode)8.0.0Web application framework
DatabasePostgreSQL14+Primary data store
JSONOj-Fast JSON serialization
HTTP ServerPuma6.xMulti-threaded web server
Rate LimitingRack::Attack-API protection
API DocsRswag-OpenAPI/Swagger generation
TestingRSpec-Test framework

Service Layer

All business logic is encapsulated in service objects under app/services/pricing/:

Core Services

ServiceResponsibility
EngineMain orchestrator - coordinates all pricing calculations
RuleMatcherFinds applicable rules for a restaurant/package
ConditionEvaluatorEvaluates if conditions match target datetime
PriceCalculatorApplies price adjustments (increase/decrease)
PriorityResolverDetermines which rules take precedence
GlobalLimitsEnforcerCaps adjustments within allowed limits

Service Pattern

All services inherit from BaseService and follow a consistent pattern:

module Pricing
  class Engine < BaseService
    def initialize(restaurant_id:, original_price:, target_datetime:, **options)
      @restaurant_id = restaurant_id
      @original_price = original_price
      @target_datetime = target_datetime
      @options = options
    end

    def call
      # 1. Validate inputs
      # 2. Find matching rules
      # 3. Calculate adjustments
      # 4. Apply global limits
      # 5. Return result
      success(data: result_hash)
    rescue StandardError => e
      failure(error: e.message)
    end
  end
end

Directory Structure

app/
├── controllers/
│   ├── application_controller.rb
│   ├── health_controller.rb
│   ├── api/
│   │   ├── partner/
│   │   │   └── v1/
│   │   │       ├── base_controller.rb
│   │   │       └── pricing_rules_controller.rb
│   │   └── internal/
│   │       └── v1/
│   │           ├── base_controller.rb
│   │           └── prices_controller.rb
│   └── concerns/
│       └── error_handler.rb
├── models/
│   ├── pricing_rule.rb
│   ├── date_time_condition.rb
│   ├── condition_item.rb
│   └── pricing_rules_package.rb
├── serializers/
│   ├── pricing_rule_serializer.rb
│   ├── date_time_condition_serializer.rb
│   └── condition_item_serializer.rb
├── services/
│   ├── base_service.rb
│   └── pricing/
│       ├── engine.rb
│       ├── rule_matcher.rb
│       ├── condition_evaluator.rb
│       ├── price_calculator.rb
│       ├── priority_resolver.rb
│       └── global_limits_enforcer.rb
└── repositories/
    └── pricing_rule_repository.rb

Data Flow: Price Calculation

┌─────────────────────────────────────────────────────────────────┐
│ 1. Request arrives at Internal API                              │
│    POST /api/internal/v1/prices/calculate                       │
│    { restaurant_id, original_price, target_datetime }           │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. Pricing::Engine orchestrates                                 │
│    • Validates inputs                                           │
│    • Calls RuleMatcher to find applicable rules                 │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. RuleMatcher finds rules                                      │
│    • Filter by restaurant_id                                    │
│    • Filter by is_active = true                                 │
│    • Filter by valid_from/valid_until                           │
│    • Filter by package_id (if provided)                         │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 4. ConditionEvaluator checks each rule's conditions             │
│    • Match date_type against target_datetime                    │
│    • Check days_of_week (if applicable)                         │
│    • Check condition_items date/time ranges                     │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 5. PriorityResolver determines which rules apply                │
│    • highest_priority: Only top-priority rule                   │
│    • stack_all: All matching rules in priority order            │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 6. PriceCalculator applies adjustments                          │
│    • percentage: (price * value / 10000)                        │
│    • fixed: ± value (in cents)                                  │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 7. GlobalLimitsEnforcer caps result (if limits provided)        │
│    • max_increase_percentage                                    │
│    • max_decrease_percentage                                    │
│    • min_price / max_price                                      │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 8. Return result                                                │
│    {                                                            │
│      original_price: 10000,  # $100.00                          │
│      final_price: 11500,     # $115.00                          │
│      total_adjustment: 1500, # $15.00                           │
│      applied_rules: [...]                                       │
│    }                                                            │
└─────────────────────────────────────────────────────────────────┘

Key Design Principles

1. Service Objects

  • Single responsibility per service
  • Dependency injection via constructor
  • Consistent return type (Result object)

2. Integer Cents

  • All monetary values stored as integers
  • Eliminates floating-point precision issues
  • Industry standard (Stripe, etc.)

3. Extensibility

  • Rule types are pluggable
  • New condition types can be added without schema changes
  • Strategy pattern for calculation strategies

4. Performance

  • YJIT enabled for 15-25% speedup
  • Efficient database queries with proper indexes
  • Eager loading to prevent N+1 queries