Backend Development 13 min read

Mastering LiteFlow: Build Elegant Java Workflow Engines for Complex Business Logic

This article introduces LiteFlow, a lightweight Java workflow engine, explains its core features, demonstrates rule syntax for serial, parallel, switch, and conditional orchestration, and provides step‑by‑step integration examples—including Maven dependencies, configuration, component implementation, and controller usage—to simplify complex business processes.

macrozheng
macrozheng
macrozheng
Mastering LiteFlow: Build Elegant Java Workflow Engines for Complex Business Logic

LiteFlow Overview

LiteFlow is a lightweight, powerful Chinese workflow engine framework designed for orchestrating complex component‑based business logic. It allows developers to define business logic in separate components and connect them using concise rule files.

Main features include:

Unified component definition using Spring native

@Component

annotation.

Lightweight rule files; beginners can learn the expression language in about five minutes.

Support for XML, JSON, and YAML rule file formats.

Flexible orchestration of synchronous and asynchronous processes.

Rule sources can be loaded from local files or Zookeeper, with extensible interfaces.

Hot‑refresh mechanism that applies rule changes without restarting the application.

Broad compatibility with Spring Boot, Spring, or other Java projects.

IDEA Plugin

LiteFlow provides an IDEA plugin

LiteFlowX

that offers intelligent rule file suggestions, syntax highlighting, navigation between components and rule files, and a toolbox for quick access.

Rule Expressions

Key keywords enable various orchestration patterns:

THEN – Serial execution (e.g.,

THEN(a, b, c, d);

).

WHEN – Parallel execution (e.g.,

WHEN(a, b, c);

).

SWITCH – Switch logic based on a component’s result (e.g.,

SWITCH(a).to(b, c, d);

).

IF – Conditional execution (e.g.,

IF(x, a);

).

ELSE – Equivalent to

IF

with else branch (e.g.,

IF(x, a).ELSE(b);

).

ELIF – Else‑if chaining (e.g.,

IF(x1, a).ELIF(x2, b).ELSE(c);

).

Sub‑processes can be defined and invoked to keep complex flows clear.

Integration Example

To integrate LiteFlow into a Spring Boot project, add the starter dependency:

<code>&lt;dependency&gt;
    &lt;groupId&gt;com.yomahub&lt;/groupId&gt;
    &lt;artifactId&gt;liteflow-spring-boot-starter&lt;/artifactId&gt;
    &lt;version&gt;2.8.5&lt;/version&gt;
&lt;/dependency&gt;</code>

Configure the rule file location in

application.yml

:

<code>server:
  port: 8580
liteflow:
  # Rule file path
  rule-source: liteflow/*.el.xml</code>

Define components by extending

NodeComponent

and implementing

process()

. Example of a coupon discount component:

<code>@Component("couponCmp")
public class CouponCmp extends NodeComponent {
    @Override
    public void process() throws Exception {
        PriceContext context = this.getContextBean(PriceContext.class);
        Long couponId = context.getCouponId();
        BigDecimal couponPrice = new BigDecimal(15);
        BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
        BigDecimal currPrice = prePrice.subtract(couponPrice);
        context.addPriceStep(new PriceStepVO(PriceTypeEnum.COUPON_DISCOUNT,
                couponId.toString(), prePrice,
                currPrice.subtract(prePrice), currPrice,
                PriceTypeEnum.COUPON_DISCOUNT.getName()));
    }
    @Override
    public boolean isAccess() {
        return this.getContextBean(PriceContext.class).getCouponId() != null;
    }
}</code>

Conditional components can extend

NodeSwitchComponent

and implement

processSwitch()

to decide the next node based on context data.

<code>@Component("postageCondCmp")
public class PostageCondCmp extends NodeSwitchComponent {
    @Override
    public String processSwitch() throws Exception {
        PriceContext context = this.getContextBean(PriceContext.class);
        return context.isOversea() ? "overseaPostageCmp" : "postageCmp";
    }
}</code>

Define rule files for sub‑processes and the main flow, for example:

<code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;flow&gt;
    &lt;chain name="promotionChain"&gt;
        THEN(fullCutCmp, fullDiscountCmp, rushBuyCmp);
    &lt;/chain&gt;
&lt;/flow&gt;</code>
<code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;flow&gt;
    &lt;chain name="mainChain"&gt;
        THEN(
            checkCmp, slotInitCmp, priceStepInitCmp,
            promotionConvertCmp, memberDiscountCmp,
            promotionChain, couponCmp,
            SWITCH(postageCondCmp).to(postageCmp, overseaPostageCmp),
            priceResultCmp, stepPrintCmp
        );
    &lt;/chain&gt;
&lt;/flow&gt;</code>

Expose an endpoint that invokes the flow via

FlowExecutor

:

<code>@Controller
public class PriceExampleController {
    @Resource
    private FlowExecutor flowExecutor;

    @RequestMapping(value = "/submit", method = RequestMethod.POST)
    @ResponseBody
    public String submit(@Nullable @RequestBody String reqData) {
        try {
            PriceCalcReqVO req = JSON.parseObject(reqData, PriceCalcReqVO.class);
            LiteflowResponse response = flowExecutor.execute2Resp("mainChain", req, PriceContext.class);
            return response.getContextBean(PriceContext.class).getPrintLog();
        } catch (Throwable t) {
            t.printStackTrace();
            return "error";
        }
    }
}</code>

The shared

PriceContext

object stores all intermediate data, such as order details, member information, coupon IDs, promotion packs, and price steps, enabling components to read and write without explicit parameter passing.

<code>public class PriceContext {
    private String orderNo;
    private boolean oversea;
    private List<ProductPackVO> productPackList;
    private OrderChannelEnum orderChannel;
    private String memberCode;
    private Long couponId;
    private List<PromotionPackVO> promotionPackList;
    private List<PriceStepVO> priceStepList = new ArrayList<>();
    private BigDecimal originalOrderPrice;
    private BigDecimal finalOrderPrice;
    private String printLog;
    // getters and setters omitted for brevity
}</code>

By using LiteFlow, complex business processes become clear, maintainable, and easily extensible, with rule files that are simple to write and hot‑reloadable without restarting the application.

Javabackend developmentLiteFlowprocess orchestrationWorkflow Engine
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.