3 minute read

Build ERP long enough and a certain despair sets in. Define the entity, write the repository, write the service, write the controller, build the screen, wire up permissions, add Excel export — and then do exactly the same thing again for the next domain. Two hundred domains, two hundred times.

Looking for a way out of that repetition, I found Erupt, an open-source low-code framework (Apache 2.0). Its core idea — generating full CRUD at runtime from annotation metadata — was precisely what I wanted. Rather than building from scratch, I took that engine as the foundation and extended it into our company’s platform. That became HunikFlow.

@Flow is all it takes

You declare annotations on a JPA entity:

@Flow(name = "Customer Management")
@Entity
public class Customer {
    @FlowField(
        views = @View(title = "Customer Name"),
        edit = @Edit(title = "Customer Name", notNull = true),
        search = @Search
    )
    private String name;
    ...
}

That’s it. At runtime the metadata is read, and:

  • A REST API opens automatically (a universal /api/v1/data/{flowName} endpoint)
  • The Angular admin screen — table, search, form — renders from the same metadata
  • Row- and column-level permissions and Excel export come along with it

The key point is that this is annotation-metadata-driven runtime CRUD, not a drag-and-drop form builder. The code is still Java, still version-controlled in Git, still autocompleted by your IDE. The goal isn’t “no code,” it’s “no repetition.”

Of course you always hit the point where generation alone isn’t enough. Extension hooks — @DataProxy (pre/post-save logic), @PowerHandler (dynamic permissions), @OnChange (field interlocks) — let you drop into Java only for the parts that need it.

What we added on top

We built what we needed on top of the base engine. Three areas were missing or thin in the original.

Business domains. Nine ERP modules (CRM, finance, HR, inventory, commerce, assets, CS, and more) and three MES modules, built directly on the engine. The MES side goes deep — OEE (overall equipment effectiveness), SPC control charts (Cp/Cpk with all eight Nelson Rules), BOM, MRP, and Andon. To break the impression that “low-code is a toy,” you have to show it digesting a real manufacturing-floor domain.

An AI module. A new module integrating 14 LLM providers, supporting function calls and SSE streaming, so you can hold a conversation with an AI over your data from inside a business screen.

The engine itself. A BPM approval workflow suited to Korean business practice, a GraalJS dynamic API (write a script in the UI and it registers immediately as a REST endpoint), and a modernization of the whole codebase (Java 21, Angular 21, PostgreSQL 17).

Scale and tooling

  • 46 Maven modules, 1,166 Java sources (~98,000 lines), 217 entities
  • 795+ tests, with CodeQL/Qodana static analysis running continuously
  • Four swappable UI renderers (Ant Design, Element UI, and others)

On building atop open source

The biggest lesson from this project: owning your foundation makes the story stronger, not weaker. There’s a temptation to say you built it all yourself, but the real engineering value lives elsewhere — in the judgment to pick a good foundation, the skill to understand its structure deeply and extend it without invasive changes, and the follow-through to complete an actual business domain (ERP, MES) on top. It’s the same shape of story as KoCoinEx, which started from BIZZAN and got re-platformed onto Go + Rust.

Those 795 tests were the safety net for that extension work. In a codebase where changing one line of the engine touches 200 domains, I wouldn’t have attempted an extension of this scale without building implementation and tests in the same breath alongside Claude Code.

Updated: