Sign inSign up

maifeeulasad/fluid-builder

By maifeeulasad

โ€ขUpdated 17 days ago

Java 24 microservice framework for Kafka. No build tool: annotate a method, ship your .java files.

Image
Developer tools
0

2.0K

maifeeulasad/fluid-builder repository overview

โ ๐ŸŒŠ Fluid

๐Ÿš€ A tiny but agile microservice framework built in Java 24 with first-class support for Docker ๐Ÿณ, Kubernetes โ˜ธ๏ธ, and Kafka ๐Ÿ“จ event streaming. Built for speed, scale, and simplicity.


โ โœจ Features

โœ… Java 24-powered lightweight core โœ… ๐Ÿ” Kafka-based event-driven architecture โœ… ๐Ÿณ Docker-ready containers โœ… โ˜ธ๏ธ Kubernetes-deployable out of the box โœ… ๐Ÿ” Minimal boilerplate, maximum flexibility โœ… ๐Ÿ”ง DIY microservice stack for builders and hackers โœ… ๐Ÿ˜ 100% open source

No build tool. There is no Maven or Gradle step to ship a service. Your .java files are copied into the image and compiled at container start.


โ ๐Ÿ“ฆ Getting Started

A service is a Dockerfile, a pom.xml listing your dependencies, and your Java files. Nothing else.

FROM maifeeulasad/fluid-builder:latest

COPY pom.xml .
COPY *.java .

At container start Fluid downloads the dependencies your pom.xml names, compiles your sources, finds your listeners, and starts consuming.

โ Write a listener ๐ŸŽง

Any class with an annotated method is picked up โ€” the file name does not matter.

public class MessageService {

    @KafkaListener(topic = "orders", groupId = "order-processors")
    public void handleOrder(String message) {
        System.out.println("๐Ÿ“ฅ " + message);
    }
}
โ Send messages ๐Ÿ“ค

To write a producer instead of a consumer, supply your own Fluid.java. It replaces the framework's entry point.

public class Fluid {
    public static void main(String[] args) throws InterruptedException {
        try {
            for (int i = 0; i < 1000; i++) {
                KafkaMessenger.sendMessage("orders", "Message " + i);
            }
        } finally {
            KafkaMessenger.shutdown();
        }
    }
}

โ ๐Ÿงฉ Annotations

โ @KafkaListener โ€” the common case
@KafkaListener(topic = "orders", groupId = "order-processors")
public void handleOrder(String message) { }
AttributeDefault
topicrequiredtopic to consume
groupIdrequiredconsumer group to join
bootstrapServersinheritsbroker address; blank follows BOOTSTRAP_SERVERS
โ @KafkaSubscription โ€” full control

Same job, with batching, dead letter routing, topic creation and consumer tuning. Use it when you need one of those; @KafkaListener otherwise.

@KafkaSubscription(
    topic = "orders",
    groupId = "order-processors",
    partitions = 5,
    batchEnabled = true,
    enableDeadLetterQueue = true,
    deadLetterTopic = "order-errors",
    maxPollRecords = 100
)
public void handleOrder(String message) { }

Some attributes are accepted but not yet honoured โ€” each is marked not yet honoured in its javadoc, so a setting that does nothing says so rather than pretending.

A method may carry @KafkaListener or @KafkaSubscription, not both: each drives its own consumer, so the handler would run twice per record. Startup rejects it.

โ @SendTo โ€” forward the result
@KafkaListener(topic = "orders", groupId = "order-processors")
@SendTo(topic = "processed-orders")
public String handleOrder(String message) {
    return "Processed: " + message;
}
โ @ShortCircuit โ€” route failures
@KafkaListener(topic = "orders", groupId = "order-processors")
@ShortCircuit(topic = "order-errors")
public void handleOrder(String message) {
    if (message.contains("fail")) {
        throw new RuntimeException("bad message");
    }
}

โ โš™๏ธ Configuration

VariableDefault
BOOTSTRAP_SERVERSkafka:9092broker address for everything that does not set one explicitly
KAFKA_COMPRESSION_TYPEgzipproducer codec

gzip is the default because it is the only compressing codec that works with the dependency set Fluid downloads. snappy, lz4 and zstd need third-party libraries that kafka-clients does not bundle โ€” select one and Fluid tells you which library to add rather than failing later at send time.


โ ๐Ÿ› ๏ธ Architecture

[Your Producer] โ”€โ”€โ–ถ KafkaMessenger โ”€โ”€โ–ถ [Kafka Broker] โ”€โ”€โ–ถ MessageConsumer โ”€โ”€โ–ถ [Your Listener]
                    MessageProducer                       KafkaProcessor
  • ๐Ÿงฉ Handlers are found by annotation at startup, not by file name
  • ๐Ÿงต Records are handled on the poll thread, so per-partition order holds
  • โœ… Offsets are committed only after a record has been processed
  • ๐Ÿ›‘ A failing handler leaves its partition uncommitted and rewinds to retry, rather than committing past the failure

โ ๐Ÿงช Building and testing

Run it the way it ships:

java DependencyDownloader.java   # populates lib/
java -cp "lib/*" Fluid.java

Run the tests:

mvn test

Maven exists for tests, linting and CI only โ€” its output is never shipped, and it compiles the same flat sources in place. See DEVELOPMENT.md.


โ ๐Ÿ”ฎ Roadmap

  • ๐Ÿ›‘ Graceful shutdown hooks
  • ๐Ÿ” At-least-once delivery with per-partition ordering
  • ๐Ÿ’€ Dead letter routing
  • ๐Ÿ“Š Metrics (Prometheus or Micrometer)
  • ๐Ÿ’พ Configuration via fluid.yaml
  • ๐Ÿง  Built-in retry and backoff strategy
  • ๐Ÿ”€ Parallel handling that preserves per-partition order

โ ๐Ÿค Contributing

PRs are welcome! Open an issue or suggest an improvement โ€” let's make microservices fun and fast again ๐Ÿงช


โ ๐Ÿ“œ License

MIT License ยฉ 2026 Maifee Ul Asad

Tag summary

Content type

Image

Digest

sha256:a8533db4eโ€ฆ

Size

501.5 MB

Last updated

17 days ago

docker pull maifeeulasad/fluid-builder