# Rule Engine Design Specification

**Date:** 2026-05-24
**Status:** Approved
**Author:** Claude Sonnet 4.6
**Project:** KSU App (Laravel Backend + Flutter Mobile)

---

## 1. Overview

### Purpose
Build a multi-domain rule engine with UI-driven JSON schema configuration, allowing business users to create, test, and manage rules without technical knowledge.

### Scope
- **4 domains:** Loan Risk, Employee Performance, Attendance, Incentive/Payroll
- **Target users:** Business users (non-technical) who create and manage rules frequently
- **Complexity:** Advanced — computed fields, cross-table reference, temporal logic
- **Architecture:** JSON Schema-driven UI + Custom PHP Evaluator

### Goals
1. Business user dapat build rules via visual UI tanpa perlu tahu JSON
2. UI driven oleh JSON Schema — extensible untuk domain baru tanpa modify core
3. Evaluator support computed fields, temporal logic, dan cross-domain reference
4. Dry-run/preview sebelum activate — user bisa test impact sebelum save
5. Versioning dan history — track perubahan rule

---

## 2. Architecture

```
┌─────────────────────────────────────────────────────────┐
│                    Flutter Mobile App                    │
│  ┌──────────────┐    ┌──────────────────────┐          │
│  │ RuleBuilderUI │───▶│  JSON Schema Engine  │          │
│  │  (Visual)    │    │  (renders form from  │          │
│  └──────────────┘    │   schema dynamically) │          │
│         │            └──────────────────────┘          │
│         ▼                       │                       │
│  ┌──────────────┐              │ saves JSON            │
│  │RuleDefinition│──────────────┘                       │
│  │    Model     │                                       │
│  └──────┬───────┘                                       │
└─────────┼───────────────────────────────────────────────┘
          │ POST /api/rule-engine/rules
          ▼
┌─────────────────────────────────────────────────────────┐
│                    Laravel Backend                       │
│  ┌─────────────────┐    ┌─────────────────────────────┐ │
│  │ RuleEngine     │───▶│  RuleEvaluator Engine       │ │
│  │ Controller     │    │  - computed_fields          │ │
│  └─────────────────┘    │  - cross_table_ref          │ │
│                         │  - temporal_logic           │ │
│                         │  - outcome_executor         │ │
│                         └─────────────────────────────┘ │
│                                   │                     │
│                         ┌─────────▼──────────┐         │
│                         │rule_engine_definitions│      │
│                         │(JSON stored as TEXT) │       │
│                         └─────────────────────┘         │
│                                   │                     │
│                         ┌─────────▼──────────┐         │
│                         │ FactResolverRegistry│        │
│                         │ - LoanFactResolver │        │
│                         │ - EmployeeResolver │        │
│                         │ - AttendanceResolver│       │
│                         │ - PayrollResolver  │         │
│                         └─────────────────────┘        │
└─────────────────────────────────────────────────────────┘
```

### Key Design Decisions

1. **UI driven by JSON Schema** — tidak hardcoded field. Tambah domain baru cukup tambah schema definition
2. **Rule stored as JSON** — flexibility tinggi, UI dan evaluator berbagi struktur yang sama
3. **FactResolver pattern** — two modes:
   - **Option A:** Caller passes fact (workflow trigger dari PayrollService, dll)
   - **Option B:** Evaluator resolves sendiri (preview/dry-run dari UI)
4. **Cross-domain delegation** — Payroll resolver bisa call Employee resolver, dll

---

## 3. JSON Schema Design

### Rule Definition Schema

```json
{
  "id": "uuid",
  "name": "string",
  "description": "string",
  "fact_type": "string",
  "domain": "string",
  "priority": "integer",
  "is_active": "boolean",
  "conditions": {
    "logic": "AND | OR",
    "rules": [
      {
        "field": "string",
        "operator": "string",
        "value": "mixed",
        "value_type": "percentage | amount | count | text",
        "temporal": {
          "type": "rolling | fixed | period",
          "period": "string",
          "start_date": "date",
          "end_date": "date"
        }
      }
    ]
  },
  "outcome": {
    "type": "string",
    "severity": "string",
    "label": "string",
    "message": "string",
    "amount": "integer",
    "percentage": "float",
    "applied_to": "string"
  }
}
```

### Supported Operators

| Field Type | Operators |
|------------|-----------|
| percentage | `>`, `>=`, `<`, `<=`, `==`, `!=` |
| amount | `>`, `>=`, `<`, `<=`, `==`, `!=` |
| count | `>`, `>=`, `<`, `<=`, `==`, `!=`, `in`, `not_in` |
| string | `==`, `!=`, `in`, `not_in`, `contains` |
| boolean | `==`, `!=` |
| date | `>`, `>=`, `<`, `<=`, `==`, `in_range` |

### Domain & Fact Types

| Domain | Fact Type | Fields |
|--------|-----------|--------|
| loan_risk | loan_profile | npl_status, is_npl, npl_percentage, outstanding, repayment_rate, days_overdue |
| performance | employee_performance | npl_count, npl_percentage, outstanding, repayment_rate, customer_count, active_customers, target_achievement_pct |
| attendance | attendance | check_in_time, check_out_time, location_lat, location_lng, work_duration_hours, geo_distance_km, late_count_7d, late_count_30d |
| incentive | payroll | base_salary, allowances, deductions, gross_salary, net_salary, bonus, avg_bonus_3mo |

### Computed Fields

| Fact Type | Computed Field | Formula |
|-----------|---------------|---------|
| loan_profile | avg_npl_monthly | avg(npl_percentage, 3months) |
| loan_profile | max_overdue_3mo | max(days_overdue, 3months) |
| loan_profile | payment_trend | compare first_half vs second_half |
| employee_performance | avg_npl_3mo | avg(npl_percentage, 3months) |
| employee_performance | avg_npl_6mo | avg(npl_percentage, 6months) |
| employee_performance | npl_trend | improving/worsening/stable |
| employee_performance | outstanding_growth | % growth 3 bulan |
| attendance | work_duration_hours | diff_hours(check_in, check_out) |
| attendance | geo_distance_km | haversine(location, office) |
| payroll | gross_salary | base + allowances + overtime + bonus |
| payroll | net_salary | gross - deductions |

---

## 4. FactResolver Architecture

### Interface

```php
interface FactResolver {
    public function resolve(string $entityId, array $context = []): array;
    public function getAvailableFields(): array;
    public function getComputedFields(string $entityId): array;
}
```

### Resolver Registry

```
FactResolverRegistry
  ├── get(factType): FactResolver
  ├── register(factType, Resolver)
  └── resolveCrossDomain(factType, entityId, context): array
```

### Cross-Domain Delegation

Payroll resolver can delegate to Employee resolver to get NPL data for bonus calculation:

```php
public function delegateTo(string $factType, string $entityId, array $context): array
{
    return $this->registry->get($factType)->resolve($entityId, $context);
}
```

---

## 5. API Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/rule-engine/meta` | Get all fact types, fields, operators |
| GET | `/api/rule-engine/rules` | List rules with pagination & filter |
| GET | `/api/rule-engine/rules/{id}` | Get single rule |
| POST | `/api/rule-engine/rules` | Create new rule |
| PUT | `/api/rule-engine/rules/{id}` | Update rule |
| DELETE | `/api/rule-engine/rules/{id}` | Delete rule |
| POST | `/api/rule-engine/evaluate` | Evaluate rule(s) against fact |
| POST | `/api/rule-engine/rules/{id}/toggle` | Toggle active status |
| GET | `/api/rule-engine/templates` | List pre-built templates |
| GET | `/api/rule-engine/rules/{id}/history` | Get rule change history |

---

## 6. UI/UX Design

### Screen Flow

```
1. Template Selection → Pick pre-built template or start blank
2. Rule Builder Form → Configure name, domain, fact type, conditions, outcome
3. Dry Run → Test rule with real data, see impact preview
4. Activate → Save and activate rule
5. Rules Dashboard → View all active rules with statistics
```

### UX Guidance Layers

1. **Templates** — Start from pre-built templates (Loan Risk, Performance, Attendance, Incentive)
2. **Field Picker + Tooltip** — Each field has explanation and context
3. **Smart Value Suggestion** — Suggestions based on historical data
4. **Impact Preview** — Show how many records will be affected before save
5. **Dry Run** — Test with real data before activating

### Visual Components

- Domain & Fact Type cascading dropdown
- Dynamic condition builder (add/remove conditions)
- Logic selector (AND/OR)
- Operator dropdown (filtered by field type)
- Value input (type-aware: number, percentage, amount, text)
- Temporal selector (rolling, fixed, period)
- Outcome type selector (flag, bonus, deduction, etc.)
- Impact statistics card

---

## 7. Database Schema

### Existing Table (extend)

```php
// rule_engine_definitions — already exists, extend columns
Schema::create('rule_engine_definitions', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('name');
    $table->text('description')->nullable();
    $table->string('fact_type');
    $table->string('domain');
    $table->integer('priority')->default(0);
    $table->boolean('is_active')->default(false);
    $table->json('conditions');
    $table->json('outcome');
    $table->uuid('created_by')->nullable();
    $table->uuid('updated_by')->nullable();
    $table->timestamps();
    $table->softDeletes();
});
```

### New Tables

```php
// rule_engine_templates — pre-built templates
Schema::create('rule_engine_templates', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('name');
    $table->text('description')->nullable();
    $table->string('domain');
    $table->string('fact_type');
    $table->json('conditions');
    $table->json('outcome');
    $table->integer('usage_count')->default(0);
    $table->timestamps();
});

// rule_engine_history — versioning
Schema::create('rule_engine_history', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->uuid('rule_id');
    $table->uuid('changed_by');
    $table->string('action'); // created, updated, activated, deactivated, deleted
    $table->json('before'); // snapshot before change
    $table->json('after');  // snapshot after change
    $table->text('reason')->nullable();
    $table->timestamps();
});

// fact_type_definitions — metadata for UI
Schema::create('fact_type_definitions', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('domain');
    $table->string('fact_type');
    $table->string('name');
    $table->string('type'); // percentage, amount, count, string, boolean, date
    $table->string('label');
    $table->text('description')->nullable();
    $table->boolean('supports_temporal')->default(false);
    $table->json('computed_config')->nullable();
    $table->integer('sort_order')->default(0);
    $table->timestamps();
});
```

---

## 8. Implementation Priority

### Phase 1: Backend Core (Laravel)
1. Extend `rule_engine_definitions` migration (if needed)
2. Create `rule_engine_templates` migration
3. Create `rule_engine_history` migration
4. Create `fact_type_definitions` migration + seeder
5. Implement `FactResolverRegistry` + all resolvers
6. Implement `RuleEvaluator` engine
7. Extend `RuleEngineController` with new endpoints

### Phase 2: Frontend Core (Flutter)
1. Extend `rule_engine_definition.dart` model
2. Add `RuleTemplate` and `RuleHistory` models
3. Extend `RuleEngineService` API methods
4. Add `RuleTemplateStore` (MobX)
5. Create `RuleTemplateScreen` (template selection)
6. Create `RuleBuilderScreen` (rule form with dynamic conditions)
7. Create `RuleEvaluationScreen` (dry run)
8. Create `RuleDashboardScreen` (active rules list)

### Phase 3: Advanced Features
1. Condition grouping (nested AND/OR)
2. Cross-domain rule evaluation
3. Rule versioning UI
4. Notification on rule match
5. Rule scheduling (run at specific time)

---

## 9. Acceptance Criteria

1. ✅ Business user can create rule via UI without writing JSON
2. ✅ Rules stored as JSON in database
3. ✅ Evaluator supports computed fields and temporal logic
4. ✅ Dry run shows which records will be affected
5. ✅ Templates provide starting point for common rules
6. ✅ Impact preview shows statistics before save
7. ✅ Rules can be toggled active/inactive
8. ✅ History tracking for rule changes
9. ✅ Multi-domain support (Loan, Performance, Attendance, Incentive)
10. ✅ Cross-domain fact resolution (e.g., Payroll can reference Employee data)

---

## 10. Files to Modify/Create

### Backend (Laravel)

**New Files:**
- `app/Models/RuleEngineTemplate.php`
- `app/Models/RuleEngineHistory.php`
- `app/Models/FactTypeDefinition.php`
- `app/Services/FactResolver/FactResolverInterface.php`
- `app/Services/FactResolver/FactResolverRegistry.php`
- `app/Services/FactResolver/LoanFactResolver.php`
- `app/Services/FactResolver/EmployeeFactResolver.php`
- `app/Services/FactResolver/AttendanceFactResolver.php`
- `app/Services/FactResolver/PayrollFactResolver.php`
- `app/Services/RuleEvaluatorService.php`
- `app/Http/Controllers/RuleEngineController.php` (extend)
- `database/migrations//*_create_rule_engine_tables.php`
- `database/seeders/FactTypeDefinitionSeeder.php`

**Modify:**
- `app/Models/RuleEngineDefinition.php` — add scopes, computed fields support
- `app/Services/RuleEngineService.php` — extend interface
- `app/Services/RuleEngineServiceImpl.php` — implement new methods

### Frontend (Flutter)

**New Files:**
- `lib/models/rule_engine_template.dart`
- `lib/models/rule_engine_history.dart`
- `lib/models/fact_type_definition.dart`
- `lib/api/RuleTemplateService.dart`
- `lib/store/RuleTemplateStore.dart`
- `lib/screen/RuleTemplateScreen.dart`
- `lib/screen/RuleBuilderScreen.dart`
- `lib/screen/RuleEvaluationScreen.dart`
- `lib/screen/RuleDashboardScreen.dart`
- `lib/widget/RuleConditionBuilder.dart`
- `lib/widget/RuleOutcomeBuilder.dart`
- `lib/widget/FieldPickerWidget.dart`
- `lib/widget/SmartValueSuggestion.dart`
- `lib/widget/ImpactPreviewCard.dart`

**Modify:**
- `lib/models/rule_engine_definition.dart` — extend model
- `lib/api/RuleEngineService.dart` — extend API methods
- `lib/store/RuleEngineStore.dart` — extend MobX store