Product-FARM Architecture
This document provides a comprehensive overview of Product-FARM’s system architecture, component design, and data flow.
Table of Contents
- System Overview
- Component Architecture
- Data Model
- Rule Engine
- API Layer
- Persistence Layer
- Frontend Architecture
- Security Considerations
- Scalability
System Overview
Product-FARM is built as a layered architecture with clear separation of concerns:
flowchart TB
subgraph Client["CLIENT LAYER"]
direction TB
UI["React 19 + TypeScript + Vite"]
UI --> RB["Visual Rule Builder"]
UI --> DAG["DAG Canvas (@xyflow)"]
UI --> SIM["Simulation Panel"]
UI --> AI["AI Chat Assistant"]
end
subgraph API["API LAYER"]
direction TB
REST["REST API (Axum)\nPort: 8081"]
gRPC["gRPC API (Tonic)\nPort: 50051"]
REST --> Services
gRPC --> Services
subgraph Services["Service Layer"]
PS["ProductService"]
RS["RuleService"]
AS["AttributeService"]
ES["EvalService"]
end
end
subgraph Core["CORE ENGINE"]
direction LR
subgraph JL["JSON Logic"]
Parser
Compiler
BytecodeVM["Bytecode VM"]
end
subgraph RE["Rule Engine"]
DAGBuilder["DAG Builder"]
TopoSort["Topo Sort"]
Executor
end
subgraph AIA["AI Agent"]
Translator
Explainer
Validator
end
end
subgraph Persistence["PERSISTENCE LAYER"]
direction LR
DGraph["DGraph\n(Graph DB)\nPort: 9080"]
Cache["LRU Cache\n(Hot Data)"]
File["File Storage\n(Development)"]
end
Client -->|"HTTP/REST + gRPC"| API
API --> Core
Core --> Persistence
style Client fill:#1e3a5f,stroke:#3b82f6,color:#fff
style API fill:#1e3a5f,stroke:#3b82f6,color:#fff
style Core fill:#1e3a5f,stroke:#8b5cf6,color:#fff
style Persistence fill:#1e3a5f,stroke:#10b981,color:#fff
Component Architecture
Backend Crates
The Rust backend is organized as a Cargo workspace with the following crates:
backend/crates/
├── core/ # Domain types and business logic
├── json-logic/ # JSON Logic expression engine
├── rule-engine/ # DAG execution engine
├── persistence/ # Storage abstraction layer
├── api/ # REST + gRPC services
└── ai-agent/ # AI-powered rule management tools
product-farm-core
Core domain types that are shared across all crates:
// Product with lifecycle management
pub struct Product {
pub id: ProductId,
pub name: String,
pub status: ProductStatus, // Draft → PendingApproval → Active → Discontinued
pub template_type: String,
pub effective_from: DateTime,
pub expiry_at: Option<DateTime>,
pub version: u32,
}
// Rule with JSON Logic expression
pub struct Rule {
pub id: RuleId,
pub product_id: ProductId,
pub rule_type: String,
pub input_attributes: Vec<RuleInputAttribute>,
pub output_attributes: Vec<RuleOutputAttribute>,
pub compiled_expression: String, // JSON Logic
pub display_expression: String, // Human-readable
pub order_index: i32,
pub enabled: bool,
}
// Abstract attribute template
pub struct AbstractAttribute {
pub abstract_path: String,
pub product_id: ProductId,
pub component_type: String,
pub component_id: Option<String>,
pub datatype_id: String,
pub display_names: Vec<DisplayName>,
pub tags: Vec<Tag>,
pub immutable: bool,
}
// Dynamic value type
pub enum Value {
Null,
Bool(bool),
Int(i64),
Float(f64),
Decimal(Decimal),
String(String),
Array(Vec<Value>),
Object(HashMap<String, Value>),
}
product-farm-json-logic
High-performance JSON Logic implementation with tiered compilation:
flowchart TB
Input["JSON Logic Expression"] --> Parser["Parser\n(JSON → AST)"]
Parser --> Validator["Validator\n(Syntax OK)"]
Parser --> Evaluator["Evaluator\n(High-level)"]
Evaluator --> Tier0["Tier 0 (AST)\n~1.15µs"]
Evaluator --> Tier1["Tier 1 (Bytecode)\n~330ns"]
Evaluator --> Tier2["Tier 2 (JIT)\nFuture"]
Tier0 -->|"Auto-promote after 100 evals"| Tier1
style Input fill:#312e81,stroke:#6366f1,color:#fff
style Parser fill:#1e3a5f,stroke:#3b82f6,color:#fff
style Validator fill:#1e3a5f,stroke:#3b82f6,color:#fff
style Evaluator fill:#1e3a5f,stroke:#3b82f6,color:#fff
style Tier0 fill:#fef3c7,stroke:#f59e0b,color:#000
style Tier1 fill:#d1fae5,stroke:#10b981,color:#000
style Tier2 fill:#e5e7eb,stroke:#6b7280,color:#000
Supported Operations:
| Category | Operations |
|---|---|
| Arithmetic | +, -, *, /, %, min, max |
| Comparison | ==, !=, <, <=, >, >=, ===, !== |
| Logic | and, or, !, !!, if |
| Arrays | map, filter, reduce, all, some, none, merge, in |
| Strings | cat, substr, in |
| Data | var, missing, missing_some |
product-farm-rule-engine
DAG-based execution engine:
pub struct RuleDag {
graph: DiGraph<RuleNode, ()>,
nodes: HashMap<RuleId, NodeIndex>,
}
impl RuleDag {
// Build DAG from rules
pub fn build(rules: &[Rule]) -> Result<Self, DagError>;
// Detect cycles
pub fn has_cycles(&self) -> bool;
// Get topologically sorted execution order
pub fn execution_order(&self) -> Vec<RuleId>;
// Get parallel execution levels
pub fn execution_levels(&self) -> Vec<Vec<RuleId>>;
// Generate visualization
pub fn to_dot(&self) -> String;
pub fn to_mermaid(&self) -> String;
pub fn to_ascii(&self) -> String;
}
Execution Flow:
flowchart TB
Input["Rules Input"] --> DAGBuilder["DAG Builder\n• Parse rules\n• Extract deps\n• Build graph"]
DAGBuilder --> CycleCheck["Cycle Check\n(Tarjan's SCC)"]
CycleCheck -->|"No cycles"| TopoSort["Topological Sort\n(Kahn's Algorithm)"]
TopoSort --> LevelAssign["Level Assignment\n(BFS by depth)"]
LevelAssign --> L0["Level 0: [Rule1, Rule2, Rule3]\n⚡ Parallel"]
LevelAssign --> L1["Level 1: [Rule4, Rule5]\n⚡ Parallel"]
LevelAssign --> L2["Level 2: [Rule6]\n→ Sequential"]
L0 --> Result["ExecutionResult"]
L1 --> Result
L2 --> Result
style Input fill:#312e81,stroke:#6366f1,color:#fff
style DAGBuilder fill:#1e3a5f,stroke:#3b82f6,color:#fff
style CycleCheck fill:#1e3a5f,stroke:#3b82f6,color:#fff
style TopoSort fill:#1e3a5f,stroke:#3b82f6,color:#fff
style LevelAssign fill:#1e3a5f,stroke:#3b82f6,color:#fff
style L0 fill:#d1fae5,stroke:#10b981,color:#000
style L1 fill:#d1fae5,stroke:#10b981,color:#000
style L2 fill:#fef3c7,stroke:#f59e0b,color:#000
style Result fill:#8b5cf6,stroke:#6366f1,color:#fff
product-farm-persistence
Multi-backend storage abstraction:
// Repository traits
#[async_trait]
pub trait ProductRepository {
async fn save(&self, product: &Product) -> Result<Product>;
async fn find_by_id(&self, id: &ProductId) -> Result<Option<Product>>;
async fn find_all(&self) -> Result<Vec<Product>>;
async fn delete(&self, id: &ProductId) -> Result<()>;
}
// Similar traits for Rule, Attribute, DataType, etc.
// Storage backends
pub enum StorageBackend {
InMemory, // HashMap-based, for testing
File, // JSON files in directory
DGraph, // Graph database
Hybrid, // DGraph + LRU cache
}
product-farm-api
Dual-protocol API layer:
flowchart TB
subgraph API["API Layer"]
direction TB
subgraph Protocols["Protocol Handlers"]
REST["REST (Axum)\n/api/products\n/api/rules\n/api/attributes\n/api/datatypes\n/api/evaluate"]
gRPC["gRPC (Tonic)\nProductService\nRuleService\nEvaluationService\nAttributeService"]
end
subgraph Shared["Shared Service Layer"]
PS["ProductService"]
RS["RuleService"]
ES["EvalService"]
end
REST --> Shared
gRPC --> Shared
end
style API fill:#1e3a5f,stroke:#3b82f6,color:#fff
style Protocols fill:#1e293b,stroke:#475569,color:#fff
style Shared fill:#1e293b,stroke:#8b5cf6,color:#fff
product-farm-ai-agent
AI-powered rule management tools:
pub struct RuleAgent {
translator: RuleTranslator, // NL → JSON Logic
explainer: RuleExplainer, // JSON Logic → NL
validator: RuleValidator, // Syntax + semantic validation
visualizer: RuleVisualizer, // DAG visualization
}
impl RuleAgent {
// Translate natural language to rule
pub async fn create_rule(&self, description: &str) -> Result<Rule>;
// Explain rule in plain English
pub fn explain_rule(&self, rule: &Rule) -> String;
// Validate rule integrity
pub fn validate_rule(&self, rule: &Rule) -> ValidationResult;
// Test rule with sample inputs
pub fn test_rule(&self, rule: &Rule, inputs: &Value) -> TestResult;
}
Data Model
Entity Relationships
erDiagram
PRODUCT ||--o{ RULE : contains
PRODUCT ||--o{ ABSTRACT_ATTRIBUTE : has
PRODUCT ||--o{ FUNCTIONALITY : defines
ABSTRACT_ATTRIBUTE }|--|| DATATYPE : typed_by
ABSTRACT_ATTRIBUTE }o--o{ TAG : tagged_with
DATATYPE ||--o| ENUMERATION : may_reference
FUNCTIONALITY ||--o{ ABSTRACT_ATTRIBUTE : requires
RULE }|--|{ ABSTRACT_ATTRIBUTE : "inputs/outputs"
PRODUCT {
string id PK
string name
enum status
string template_type
}
RULE {
string id PK
string rule_type
json expression
array inputs
array outputs
}
ABSTRACT_ATTRIBUTE {
string abstract_path PK
string datatype_id FK
array tags
boolean immutable
}
FUNCTIONALITY {
string id PK
string name
array required_attributes
enum status
}
DATATYPE {
string id PK
string primitive
json constraints
}
ENUMERATION {
string name PK
array values
string template_type
}
TAG {
string name PK
int order_index
}
Product Lifecycle
stateDiagram-v2
[*] --> Draft
Draft --> PendingApproval: submit()
PendingApproval --> Draft: reject()
PendingApproval --> Active: approve()
Active --> Discontinued: discontinue()
Draft --> Draft: clone()
Active --> Draft: clone()
note right of Active: Immutable\n(read-only)
note right of Discontinued: Preserved for\naudit purposes
Rule Engine
JSON Logic Processing Pipeline
sequenceDiagram
participant Input as JSON Logic Input
participant Parser as Parser
participant AST as AST Builder
participant Compiler as Compiler
participant VM as Stack VM
Note over Input: {"if": [{">": [{"var": "age"}, 60]}, 1.2, 1.0]}
Input->>Parser: Step 1: PARSING
Parser->>Parser: serde_json::from_str
Parser->>AST: JsonValue
AST->>AST: Step 2: AST CONSTRUCTION
Note over AST: Expression::If {<br/>condition: Comparison(>,age,60)<br/>then: Literal(1.2)<br/>else: Literal(1.0)}
AST->>Compiler: Step 3: COMPILATION (if promoted)
Note over Compiler: Bytecode:<br/>[LoadVar, LoadConst(60),<br/>Gt, JumpIfFalse, ...]
Compiler->>VM: Step 4: EXECUTION
Note over VM: Context: {age: 65}<br/>Stack: [65] → [65,60] → [true]<br/>Result: 1.2
Detailed Execution Flow:
| Step | Instruction | Stack State | Notes |
|---|---|---|---|
| 1 | LoadVar(0) |
[65] |
Load age from context |
| 2 | LoadConst(60) |
[65, 60] |
Push constant 60 |
| 3 | Gt |
[true] |
65 > 60 = true |
| 4 | JumpIfFalse(3) |
[true] |
No jump (condition true) |
| 5 | LoadConst(1.2) |
[1.2] |
Push result |
| 6 | Return |
[] |
Return 1.2 |
DAG Execution
Rules:
- R1:
base_premium = coverage * 0.02 - R2:
age_factor = if(age > 60) 1.2 else 1.0 - R3:
smoker_factor = if(smoker) 1.5 else 1.0 - R4:
premium = base_premium * age_factor * smoker_factor
flowchart TB
subgraph Inputs["Input Variables"]
coverage["coverage\n250000"]
age["age\n65"]
smoker["smoker\nfalse"]
end
subgraph Level0["Level 0 (Parallel Execution ⚡)"]
R1["R1: base_premium\ncoverage × 0.02\n= 5000"]
R2["R2: age_factor\nif(65 > 60) 1.2\n= 1.2"]
R3["R3: smoker_factor\nif(false) 1.5 else 1.0\n= 1.0"]
end
subgraph Level1["Level 1 (Sequential)"]
R4["R4: premium\n5000 × 1.2 × 1.0\n= 6000"]
end
coverage --> R1
age --> R2
smoker --> R3
R1 --> R4
R2 --> R4
R3 --> R4
R4 --> Output["premium = 6000"]
style Inputs fill:#312e81,stroke:#6366f1,color:#fff
style Level0 fill:#065f46,stroke:#10b981,color:#fff
style Level1 fill:#7c2d12,stroke:#f59e0b,color:#fff
style Output fill:#8b5cf6,stroke:#6366f1,color:#fff
Execution Timeline:
| Level | Rules | Execution Mode | Results |
|---|---|---|---|
| 0 | R1, R2, R3 | Parallel | 5000, 1.2, 1.0 |
| 1 | R4 | Sequential | 6000 |
API Layer
REST API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/products |
GET | List all products |
/api/products |
POST | Create product |
/api/products/{id} |
GET | Get product by ID |
/api/products/{id} |
PUT | Update product |
/api/products/{id} |
DELETE | Delete product |
/api/products/{id}/clone |
POST | Clone product |
/api/products/{id}/submit |
POST | Submit for approval |
/api/products/{id}/approve |
POST | Approve product |
/api/products/{id}/reject |
POST | Reject product |
/api/products/{id}/rules |
GET | List product rules |
/api/products/{id}/rules |
POST | Create rule |
/api/products/{id}/evaluate |
POST | Evaluate rules |
/api/products/{id}/batch-evaluate |
POST | Batch evaluation |
/api/abstract-attributes |
GET, POST | Attribute templates |
/api/datatypes |
GET, POST | Custom datatypes |
/api/enumerations |
GET, POST | Enumerations |
gRPC Services
// Evaluation Service
service ProductFarmService {
rpc Evaluate(EvaluateRequest) returns (EvaluateResponse);
rpc BatchEvaluate(BatchEvaluateRequest) returns (BatchEvaluateResponse);
rpc EvaluateStream(stream EvaluateRequest) returns (stream EvaluateResponse);
rpc ValidateRules(ValidateRulesRequest) returns (ValidateRulesResponse);
rpc GetExecutionPlan(GetExecutionPlanRequest) returns (ExecutionPlanResponse);
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
}
// Product Management
service ProductService {
rpc CreateProduct(CreateProductRequest) returns (Product);
rpc GetProduct(GetProductRequest) returns (Product);
rpc UpdateProduct(UpdateProductRequest) returns (Product);
rpc DeleteProduct(DeleteProductRequest) returns (Empty);
rpc ListProducts(ListProductsRequest) returns (ListProductsResponse);
rpc CloneProduct(CloneProductRequest) returns (Product);
rpc SubmitProduct(SubmitProductRequest) returns (Product);
rpc ApproveProduct(ApproveProductRequest) returns (Product);
rpc RejectProduct(RejectProductRequest) returns (Product);
}
// Rule Management
service RuleService {
rpc CreateRule(CreateRuleRequest) returns (Rule);
rpc GetRule(GetRuleRequest) returns (Rule);
rpc UpdateRule(UpdateRuleRequest) returns (Rule);
rpc DeleteRule(DeleteRuleRequest) returns (Empty);
rpc ListRules(ListRulesRequest) returns (ListRulesResponse);
}
Persistence Layer
Storage Backends
flowchart TB
subgraph Backends["Storage Backend Options"]
direction LR
InMemory["InMemory\n• HashMap\n• Fast\n• No persist\n• Testing"]
File["File\n• JSON files\n• Simple\n• Portable\n• Development"]
DGraph["DGraph\n• Graph DB\n• Scalable\n• GraphQL\n• Production"]
end
InMemory --> Trait
File --> Trait
DGraph --> Trait
subgraph Trait["Repository Trait"]
save["save()"]
find["find_by_id()"]
findAll["find_all()"]
delete["delete()"]
end
style Backends fill:#1e3a5f,stroke:#3b82f6,color:#fff
style Trait fill:#1e293b,stroke:#8b5cf6,color:#fff
style InMemory fill:#fef3c7,stroke:#f59e0b,color:#000
style File fill:#dbeafe,stroke:#3b82f6,color:#000
style DGraph fill:#d1fae5,stroke:#10b981,color:#000
Hybrid Storage (Production)
flowchart TB
Request["Request"] --> Cache{"LRU Cache\n(Hot Data)"}
Cache -->|"Cache Hit\n~1µs"| Return["Return Cached"]
Cache -->|"Cache Miss"| DGraph["DGraph Query\n~1-5ms"]
DGraph --> Update["Update Cache\n& Return"]
style Request fill:#312e81,stroke:#6366f1,color:#fff
style Cache fill:#fef3c7,stroke:#f59e0b,color:#000
style Return fill:#d1fae5,stroke:#10b981,color:#000
style DGraph fill:#1e3a5f,stroke:#3b82f6,color:#fff
style Update fill:#d1fae5,stroke:#10b981,color:#000
Frontend Architecture
Component Structure
frontend/src/
├── components/
│ ├── RuleBuilder.tsx # Block-based rule editor
│ ├── RuleCanvas.tsx # DAG visualization (@xyflow)
│ ├── RuleValidator.tsx # Rule validation UI
│ ├── SimulationPanel.tsx # Rule testing interface
│ ├── BatchEvaluator.tsx # Batch evaluation UI
│ ├── AIChat.tsx # AI assistant chat
│ ├── AttributeExplorer.tsx # Attribute browser
│ └── ProductCreationWizard.tsx
│
├── pages/
│ ├── Dashboard.tsx # Overview page
│ ├── Products.tsx # Product management
│ ├── Rules.tsx # Rule management
│ ├── Attributes.tsx # Attribute management
│ ├── Datatypes.tsx # Datatype management
│ └── Enumerations.tsx # Enumeration management
│
├── services/
│ └── api.ts # REST API client
│
├── store/
│ └── index.ts # Zustand state management
│
└── types/
└── index.ts # TypeScript type definitions
State Management (Zustand)
interface AppState {
// Products
products: Product[];
selectedProduct: Product | null;
// Rules
rules: Rule[];
selectedRule: Rule | null;
// Attributes
abstractAttributes: AbstractAttribute[];
// UI State
isLoading: boolean;
error: string | null;
// Actions
fetchProducts: () => Promise<void>;
createProduct: (product: CreateProductRequest) => Promise<void>;
evaluateRules: (inputs: Record<string, any>) => Promise<EvaluationResult>;
}
Security Considerations
Input Validation
- All API inputs validated with regex patterns
- Product IDs: alphanumeric, no leading digits
- Rule types: strict pattern matching
- JSON Logic expressions: syntax validation before execution
Product Lifecycle Protection
- Active products are immutable (read-only)
- Modification requires cloning to new Draft
- Version tracking for optimistic locking
API Security (Future)
- OAuth2/OIDC authentication
- Role-based access control
- API rate limiting
- Audit logging
Security Note: In production deployments, always enable authentication, validate all inputs at API boundaries, and use TLS for all connections. Product-FARM validates JSON Logic expressions before execution to prevent injection attacks.
Scalability
Horizontal Scaling
flowchart TB
LB["Load Balancer"] --> API1["API Node 1\n(Rust)"]
LB --> API2["API Node 2\n(Rust)"]
LB --> API3["API Node 3\n(Rust)"]
API1 --> DGraph
API2 --> DGraph
API3 --> DGraph
subgraph DGraph["DGraph Cluster"]
Alpha1["Alpha 1"]
Alpha2["Alpha 2"]
Zero["Zero\n(Coordinator)"]
end
style LB fill:#8b5cf6,stroke:#6366f1,color:#fff
style API1 fill:#1e3a5f,stroke:#3b82f6,color:#fff
style API2 fill:#1e3a5f,stroke:#3b82f6,color:#fff
style API3 fill:#1e3a5f,stroke:#3b82f6,color:#fff
style DGraph fill:#065f46,stroke:#10b981,color:#fff
Performance Optimizations
- Tiered Compilation: Auto-promote hot rules to bytecode
- LRU Caching: Cache frequently accessed data
- Parallel Execution: Rules without dependencies run concurrently
- Batch Evaluation: Process multiple inputs efficiently
- Connection Pooling: Reuse database connections
Production Deployment: For maximum throughput, deploy multiple stateless API nodes behind a load balancer. Each node maintains its own LRU cache, with DGraph providing the shared source of truth.
Summary
Product-FARM’s architecture is designed for:
- Performance: Sub-millisecond rule evaluation with tiered compilation
- Scalability: Horizontal scaling with stateless API nodes
- Flexibility: Domain-agnostic design supports any business domain
- Maintainability: Clean separation of concerns across crates
- Extensibility: Pluggable storage backends and API protocols