Sign up for periodic email updates.

Write a Spring bean. Get a distributed service.

Liquid Compute looks like Spring. Constructor injection, ordinary methods, real return types. The only addition is @Liquid on the interface. From that point on, every call is routed by the proxy — local when the service runs here, over JMS when it runs on another node — with OpenTelemetry trace context carried across every hop.

You don’t pick a transport. You don’t write a controller. You don’t configure sidecars. The framework provides all of it, and the dashboard shows you exactly where work is flowing as it happens.

See the code Tour the dashboard
Liquid Compute dashboard showing three nodes with services and RPS charts

The Developer Experience

One annotation on the interface. The implementation is a normal Spring bean — no base classes, no framework APIs, no awareness of distribution. Adoption is a Maven dependency, not a migration.

The interface

One annotation. A standard Java interface. Nothing else.

PricingService.java
@Liquid(version = "1.0.0") public interface PricingService { Price calculatePrice(Order order); }

The implementation

A normal Spring bean. Constructor injection, business logic, return a value. Nothing here knows the call may cross a node boundary.

PricingServiceImpl.java
@Component public class PricingServiceImpl implements PricingService { private final Catalog catalog; private final Promotions promotions; public PricingServiceImpl(Catalog catalog, Promotions promotions) { this.catalog = catalog; this.promotions = promotions; } public Price calculatePrice(Order order) { // Business logic — no awareness of distribution var basePrice = catalog.lookup(order.itemId()); var discount = promotions.apply(order.customerId(), basePrice); return new Price(discount, order.currency()); } }

What the framework provides

When another service calls pricingService.calculatePrice(order):

Same return type, same exception semantics, same trace context — whether the call is local or remote. The framework provides transparent routing, serialization, tracing, metrics, the dashboard, and live rebalancing — all without changes to PricingServiceImpl.

A real service cascade

The demo runs three services that compose naturally: CubeService calls SquareService and MultiplyService, SquareService calls MultiplyService. The math is real (x3 = x · x2), the cascade is real, and every arrow may cross a node boundary — routed, traced, and measured transparently.

CubeService.java
@Liquid(version = "1.0.0") public interface CubeService { int cube(int x); } @Service public class CubeServiceImpl implements CubeService { private final SquareService square; private final MultiplyService mul; public CubeServiceImpl( SquareService square, MultiplyService mul) { this.square = square; this.mul = mul; } @Override public int cube(int x) { return mul.multiply( x, square.square(x)); } }
SquareService.java
@Liquid(version = "1.0.0") public interface SquareService { int square(int x); } @Service public class SquareServiceImpl implements SquareService { private final MultiplyService mul; public SquareServiceImpl( MultiplyService mul) { this.mul = mul; } @Override public int square(int x) { return mul.multiply(x, x); } }
MultiplyService.java
@Liquid(version = "1.0.0") public interface MultiplyService { int multiply(int a, int b); } @Service public class MultiplyServiceImpl implements MultiplyService { @Override public int multiply( int a, int b) { return a * b; } } // Leaf of the cascade — // the only one doing math.

The call chain

Each arrow below may be local or remote depending on the live cluster layout. The dashboard’s topology view (below) shows exactly this chain with real invocation counts and latency.

cube(3) — distributed across three nodes
cube(3) └── multiply(3, square(3)) └── multiply(3, 3) → 9 └── multiply(3, 9) → 27

Polyglot REST access — free

Every @Liquid interface is automatically exposed as an HTTP endpoint. Non-JVM clients — legacy services, mobile apps, Python scripts, plain curl — can call into a Liquid cluster without the framework. No REST controllers to write. The endpoint exists the moment the service starts.

Call cube(3) via REST

terminal
# POST a method invocation curl -X POST \ http://localhost:8081/liquid/cube-service-1.0.0/cube \ -H 'Content-Type: application/json' \ -d '{"parameters": [3]}' # Response: 27

GET works for no-argument methods, which is handy for health-check probing from anywhere a browser or curl can reach.

URL shape

routing
POST /liquid/<service-name>-<version>/<method> # service-name: derived from # the interface name # (CubeService → cube-service) # version: @Liquid(version = "...") # method: the method name # Body (positional, Jackson): {"parameters": [arg1, arg2, ...]} # Errors: 404 unknown service/method 400 wrong parameter shape 503 no active nodes (retry) 500 application exception

Trace context carries across REST

Incoming HTTP requests propagate W3C traceparent headers. Spans chain off the upstream request rather than starting a new root, so the resulting call chain appears as one continuous trace in Tempo.

terminal
# Call with trace context — the resulting span chains off this trace curl -X POST http://localhost:8081/liquid/cube-service-1.0.0/cube \ -H 'Content-Type: application/json' \ -H 'traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' \ -d '{"parameters": [3]}'

The dashboard: see your services flow

Liquid Compute ships with a live operator dashboard. Three views, one shared header for brain controls. Every screenshot below is from the working POC, running the cube/square/multiply cascade across a three-node cluster.

Cluster Layout

default view

Every node in the cluster as a card, with active services listed inside. Each service shows current RPS and average latency. Color-coded headers reveal node identity and health at a glance, and end-to-end performance charts sit below: Call Chains/sec and Avg Chain Latency.

Cluster Layout view: three nodes each running a subset of services with per-service RPS

What you see

  • Three nodes each running a subset of services
  • Color-coded service cards with method-level metrics
  • End-to-end charts: Call Chains/sec and Avg Chain Latency

What it tells you

  • Where every service runs and how it’s performing
  • Whether the cluster is balanced or carrying disproportionate load
  • Real-time impact of brain decisions on throughput

In manual mode: each service card becomes a toggle. Operators click to activate or deactivate a service on a specific node. A safety guard prevents deactivating the last node for any service.

Service Topology

call graph view

A live call graph across the cluster. Nodes appear as boxes; services live inside them; edges show method-to-method calls labeled with invocation counts, latency, and transport type. Cross-node calls (over JMS) and local calls (in-process) are clearly distinguished.

Service Topology view: call graph showing Cube → Square → Multiply cascade across nodes

What you see

  • The Cube → Square → Multiply cascade in motion
  • Each edge labeled with call count, latency, transport
  • Node-to-node bandwidth on inter-node edges

What it tells you

  • Which services talk and how often
  • Where latency accumulates across the chain
  • Whether high-affinity services are co-located

Raw Metrics

tabular view

Per-method metrics on every node: invocation count, CPU time, wall-clock time, average CPU per call, and average wall-clock per call. Node-level totals at the top, edge metrics for physical topology at the bottom.

Raw Metrics view: per-method invocation, CPU, and latency tables for every node

Use this view to spot hotspots, identify per-call overhead, and confirm work is distributed evenly across nodes.

Brain controls — in every view

The header bar gives operators live brain management without leaving the dashboard:

Grafana & Tempo — for the deep view

For long-horizon analysis, Liquid Compute exports through Prometheus and OpenTelemetry. Grafana dashboards and Tempo traces come configured out of the box.

Metrics — Prometheus / Grafana

Grafana panels showing method invocations, transport breakdown, latency percentiles, and cross-node bandwidth
  • Method Invocations/sec — per-service RPS over time
  • Edge Invocations/sec by transport — local vs. remote breakdown
  • Method wall-clock latency (p50/p95/p99) — percentile latency per service
  • Cross-node bandwidth — request and response data volume between nodes
  • Layout epoch annotations — vertical markers when the brain rebalanced, so you can correlate metric shifts with placement changes

Distributed Traces — Tempo

Grafana Tempo view with recent traces, error rate chart, and span waterfall
  • Recent traces — table of recent requests with trace ID, start time, service, method, and duration
  • Error rate — errors/sec chart (the demo shows zero errors under continuous rebalancing — the stability invariant at work)
  • Span waterfall — click any trace ID for the full hop-by-hop timing across the cluster

What it’s built with

Standard, boring infrastructure on the inside. No custom protocols, no sidecars, no separate control plane.

JC

Core framework

Java 21 · Spring Boot 3.4.4 · Apache Artemis 2.31.2 (JMS for inter-node RPC) · Hazelcast 5.3.6 (distributed state, routing table) · Gson (JSON serialization).

OT

Observability

Micrometer (metrics facade) · Prometheus · Grafana · OpenTelemetry SDK with W3C Trace Context · Grafana Tempo for trace storage and visualization.

K8

Kubernetes

JOSDK Kubernetes operator with custom CRDs (LiquidService, LiquidServiceLayout, LiquidPlan). k3d for local clusters. Brain reconciles CRDs — declarative layout.

What’s notable

No sidecar

Runs inside the application process, not alongside it. No extra containers to manage.

No custom protocol

Standard JMS for transport, standard Hazelcast (or K8s CRDs) for coordination. No proprietary wire format.

No separate control plane

The brain runs inside the application. Nothing external to deploy, monitor, or scale.

Spring-native

Spring DI, lifecycle, actuator. Adoption is a dependency, not a migration.

Ready to see your services flow?

Join the design partner program for hands-on help bringing Liquid Compute to your stack — or read the docs and try the demo cluster yourself.

Become a Partner Read the docs