# ice
**Repository Path**: waitmoon/ice
## Basic Information
- **Project Name**: ice
- **Description**: Java规则引擎-ice(用全新的思想编排规则)
针对复杂/灵活变动业务,提供一个新的抽象编排解决方案,轻量级,高性能并提供可视化操作页面
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: https://waitmoon.com
- **GVP Project**: No
## Statistics
- **Stars**: 745
- **Forks**: 219
- **Created**: 2022-06-24
- **Last Updated**: 2026-04-14
## Categories & Tags
**Categories**: rule-engine
**Tags**: 规则引擎, 可视化规则引擎, 轻量级规则引擎
## README
Ice
A lightweight, zero-dependency rule engine with visual tree-based orchestration
Documentation ·
Live Demo ·
Getting Started ·
Changelog
中文
---
## What is Ice?
Ice is a rule engine that takes a fundamentally different approach: **rules are organized as trees, not chains or tables**. Each node in the tree handles its own logic independently — modifying one node never creates cascading effects on others. Nodes communicate exclusively through a shared, thread-safe data context called **Roam**, rather than referencing each other directly.
The result is a system where business rules can be visually configured, hot-reloaded in seconds, and executed in-memory with sub-millisecond latency — all without requiring any database or middleware.
## Key Features
| Feature | Description |
|---|---|
| **Tree-Based Orchestration** | Rules organized as trees — nodes are independent, changes to one node never cascade to others |
| **Visual Web Editor** | Configure rules through an intuitive web UI with a tree editor, no DSL to learn |
| **Zero Dependencies** | No database, no message queue, no service registry — just files on disk |
| **Multi-Language SDKs** | Java, Go, and Python with full feature parity |
| **Hot Reload** | Changes take effect in seconds via automatic version polling — no restart needed |
| **High Performance** | Pure in-memory execution with sub-millisecond latency, zero network overhead |
| **Node Reuse** | Same node can be shared across multiple rule trees |
| **Parallel Execution** | Built-in parallel relation nodes for concurrent child execution |
| **Mock Debugging** | Trigger rule execution remotely from the Web UI for real-time debugging |
| **Lane / Traffic Isolation** | Branch-based rule isolation for A/B testing, canary releases, and gradual rollouts |
## How It Works
### Rule Trees
Every rule in Ice is a **tree** composed of two types of nodes:
- **Relation Nodes** — control the execution flow (similar to logical operators)
- **Leaf Nodes** — contain your actual business logic
### Relation Nodes (Control Flow)
| Type | Behavior | Short-Circuit |
|------|----------|:---:|
| **AND** | All children must return true | Yes |
| **ANY** | At least one child returns true | Yes |
| **ALL** | All children must return true | No |
| **NONE** | No child returns true | No |
| **TRUE** | Always returns true (execute all children for side effects) | No |
Each type has a **parallel** variant (`P_AND`, `P_ANY`, `P_ALL`, `P_NONE`, `P_TRUE`) that executes children concurrently.
### Leaf Nodes (Business Logic)
| Type | Return | Purpose |
|------|--------|---------|
| **Flow** | `boolean` | Conditional checks — "Is the user eligible?" |
| **Result** | `boolean` | Business operations — "Issue the coupon" |
| **None** | `void` | Side effects — queries, logging, metrics, notifications |
### Roam (Data Context)
**Roam** is the thread-safe data container that flows through the entire rule tree. Nodes read input from and write output to Roam — they never reference each other directly. This is the key to node isolation.
```
Roam
├── put("uid", 12345) // flat key-value
├── putDeep("user.level", "gold") // nested key
├── get("uid") // → 12345
└── getDeep("user.level") // → "gold"
```
- **Thread-safe**: Java `ConcurrentHashMap` / Go `sync.RWMutex` / Python `threading.RLock`
- **Deep keys**: `putDeep("a.b.c", value)` auto-creates nested structures
- **Dynamic references**: prefix a value with `@` to resolve it from Roam at runtime (e.g., `@uid` reads `roam.get("uid")`)
## Architecture
```
┌──────────────────────────────────────────────────────────────┐
│ Shared Storage (ice-data/) │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────┐ │
│ │ apps/ │ │ bases/ │ │ confs/ │ │ versions/│ │
│ └────────┘ └────────┘ └────────┘ └──────────┘ │
└──────────────────────────────────────────────────────────────┘
▲ ▲
│ Write │ Read (Poll)
│ │
┌─────────┴─────────┐ ┌───────────┴───────────┐
│ Ice Server │ │ Ice Client │
│ │ │ │
│ • Web UI │ │ • Version polling │
│ • Tree editor │ │ • Hot reload │
│ • Apply & publish │ │ • In-memory exec │
│ • Mock debugging │ │ • Mock execution │
│ │ │ • Fault tolerant │
└────────────────────┘ └───────────────────────┘
```
**Key design decisions:**
- **Server and Client are fully decoupled** — if the Server goes down, Clients continue executing rules from their in-memory cache
- **File-based communication** — no network protocol between Server and Client, just a shared directory (`ice-data/`)
- **Incremental updates** — Client polls `versions/` for changes and applies only the delta; falls back to full reload if incremental files are missing
### Deployment Options
| Mode | Description |
|------|-------------|
| **Single Machine** | Server + Client on the same host, `ice-data/` is a local directory |
| **Docker Compose** | Containerized deployment with volume mounts |
| **Distributed** | Multiple Servers/Clients sharing storage via NFS, AWS EFS, or GCP Filestore |
## Quick Start
### 1. Deploy the Server
```bash
docker run -d --name ice-server -p 8121:8121 \
-v ./ice-data:/app/ice-data waitmoon/ice-server:latest
```
Open `http://localhost:8121` to access the Web UI.
### 2. Install the Client SDK
Java
```xml
com.waitmoon.ice
ice-core
4.0.9
```
Go
```bash
go get github.com/zjn-zjn/ice/sdks/go
```
Python
```bash
pip install ice-rules
```
### 3. Define Your Leaf Nodes
Leaf nodes are where your business logic lives. Here's a simple `ScoreFlow` that checks if a value meets a threshold:
Java
```java
@Data
@EqualsAndHashCode(callSuper = true)
public class ScoreFlow extends BaseLeafFlow {
private double score;
private String key;
@Override
protected boolean doFlow(IceRoam roam) {
Number value = roam.getDeep(key);
if (value == null) {
return false;
}
return value.doubleValue() >= score;
}
}
```
Go
```go
type ScoreFlow struct {
Score float64 `json:"score" ice:"name:Score Threshold,desc:Minimum score to pass"`
Key string `json:"key" ice:"name:Roam Key,desc:Key to read from Roam"`
}
func (s *ScoreFlow) DoFlow(ctx context.Context, roam *icecontext.Roam) bool {
value := roam.ValueDeep(s.Key).Float64Or(0)
return value >= s.Score
}
```
Register it:
```go
ice.RegisterLeaf("com.example.ScoreFlow",
&ice.LeafMeta{Name: "Score Check", Desc: "Check if score meets threshold"},
func() any { return &ScoreFlow{} })
```
Python
```python
@ice.leaf("com.example.ScoreFlow", name="Score Check", desc="Check if score meets threshold")
class ScoreFlow:
score: Annotated[float, IceField(name="Score Threshold")] = 0.0
key: Annotated[str, IceField(name="Roam Key")] = ""
def do_flow(self, roam: Roam) -> bool:
value = roam.get_deep(self.key)
if value is None:
return False
return float(value) >= self.score
```
### 4. Initialize the Client and Execute
Java
```java
// Initialize
IceFileClient client = new IceFileClient(1, "./ice-data", "com.your.package");
client.start();
// Execute
IceRoam roam = IceRoam.create();
roam.setId(1L);
roam.put("uid", 12345);
roam.put("score", 95.5);
Ice.syncProcess(roam);
```
Go
```go
// Initialize
client, _ := ice.NewClient(1, "./ice-data")
client.Start()
defer client.Destroy()
// Execute
roam := ice.NewRoam()
roam.SetId(1)
roam.Put("uid", 12345)
roam.Put("score", 95.5)
ice.SyncProcess(context.Background(), roam)
```
Python
```python
# Initialize
client = ice.FileClient(app=1, storage_path="./ice-data")
client.start()
# Execute
roam = ice.Roam.create()
roam.set_id(1)
roam.put("uid", 12345)
roam.put("score", 95.5)
ice.sync_process(roam)
```
### 5. Configure Rules in the Web UI
Open the Web UI → create a rule tree → arrange your nodes → click **Apply**. Changes take effect within seconds — no restart required.
## Comparison with Traditional Rule Engines
| | Ice | Traditional (Drools, etc.) |
|---|---|---|
| **Learning Curve** | 5 minutes — visual configuration, no DSL | Requires learning rule language syntax |
| **Deployment** | Docker one-click, zero external dependencies | Requires database + middleware setup |
| **Configuration** | Web UI tree editor | Text/code-based rule files |
| **Performance** | In-memory, sub-millisecond latency | Compilation and interpretation overhead |
| **Hot Reload** | Seconds, automatic — no restart | Often requires application restart |
| **Change Impact** | Node-isolated — changes affect only that node | Cascading effects across rule chains |
| **Ops Complexity** | Single binary + file directory | Multi-component infrastructure |
## Use Cases
| Scenario | Examples |
|----------|---------|
| **Marketing Campaigns** | Coupons, discounts, promotions, group deals, flash sales |
| **Risk Control** | Credit assessment, anti-fraud detection, real-time risk evaluation |
| **Dynamic Pricing** | Price strategies, discount rules, tiered pricing, surge pricing |
| **Access Control** | Permission management, feature flags, role-based access |
| **Process Orchestration** | Approval workflows, order processing, ticket routing, state machines |
## Configuration Reference
### Client Configuration
| Parameter | Required | Default | Description |
|-----------|:---:|---------|-------------|
| `app` | Yes | — | Application ID |
| `storagePath` | Yes | — | Path to shared `ice-data/` directory |
| `scan` | Java only | — | Package path for automatic node scanning |
| `pollInterval` | No | `2s` | How often to check for version changes |
| `heartbeatInterval` | No | `10s` | Heartbeat reporting interval |
| `parallelism` | No | ForkJoinPool | Thread pool size for parallel nodes |
| `lane` | No | — | Lane name for traffic isolation / branch testing |
### Server Configuration
| Parameter | Default | Description |
|-----------|---------|-------------|
| `port` | `8121` | Server HTTP port |
| `storage-path` | `./ice-data` | File storage directory |
| `mode` | `open` | `open` (normal) or `controlled` (read-only UI) |
| `client-timeout` | `30s` | Client inactivity timeout |
| `version-retention` | `1000` | Number of version files to retain |
| `publish-targets` | — | Remote server addresses for multi-instance publishing |
## Documentation
**Guide**
- [Getting Started](https://waitmoon.com/en/guide/getting-started.html) — Deploy, integrate, and run your first rule
- [Core Concepts](https://waitmoon.com/en/guide/concepts.html) — Trees, nodes, Roam, and execution model
- [Architecture](https://waitmoon.com/en/guide/architecture.html) — Design decisions and deployment patterns
- [FAQ](https://waitmoon.com/en/guide/faq.html) — Common questions and troubleshooting
**Reference**
- [Node Types](https://waitmoon.com/en/reference/node-types.html) — All relation and leaf node types in detail
- [Client Configuration](https://waitmoon.com/en/reference/client-config.html) — Full client parameter reference
- [Server Configuration](https://waitmoon.com/en/reference/server-config.html) — Full server parameter reference
- [Roam API](https://waitmoon.com/en/reference/roam-api.html) — Data context API across all SDKs
**SDK Guides**
- [Java SDK](https://waitmoon.com/en/sdk/java.html)
- [Go SDK](https://waitmoon.com/en/sdk/go.html)
- [Python SDK](https://waitmoon.com/en/sdk/python.html)
## Community
- [GitHub Issues](https://github.com/zjn-zjn/ice/issues) — Bug reports and feature requests
- [GitHub Discussions](https://github.com/zjn-zjn/ice/discussions) — Questions and community discussions
- [Live Demo](https://eg.waitmoon.com) — Try Ice without installing anything
## Contributing
Contributions are welcome! Whether it's bug reports, feature requests, documentation improvements, or code contributions — feel free to open an issue or submit a pull request.
## License
[Apache License 2.0](LICENSE)