Nano
tiny java tool - back to basics!
Install / Use
/learn @NanoNative/NanoREADME
🧬 Project Nano
[![Issues][issues_shield]][issues_link] [![Commit][commit_shield]][commit_link] [![License][license_shield]][license_link] [![Central][central_shield]][central_link] [![Tag][tag_shield]][tag_link] [![Javadoc][javadoc_shield]][javadoc_link] [![Size][size_shield]][size_shield] ![Label][label_shield] ![Label][java_version]
Introduction | Core Concept | Mechanics | Components | Getting Started | Build Nano | Benefits
🖼️ Introduction
Back to basics and forget about frameworks!
Nano is a lightweight approach which makes it easier for developer to write microservices in functional, fluent, chaining, plain, modern java with a nano footprint. It breaks away from traditional OOP patterns. Instead of creating complex object hierarchies with Controllers, Services, and Repositories, Nano uses static event listeners that react to events in a functional, stateless manner.
Key Philosophy:
- Static Methods, Not Objects: Business logic lives in static methods, not in service objects
- Event-Driven Communication: Everything communicates through events, not direct method calls
- Universal Services: Services are generic connectors for external systems (databases, HTTP, queues) - no business logic
- TypeMap Everywhere: Built-in type conversion and data transformation using TypeMap
- Global Error Handling: Even errors are events that can be subscribed to and handled globally
Nano is designed to be fully compilable with GraalVM to create native executables and utilizes non-blocking virtual threads from Project Loom for maximum performance.
📐 Core Concept
Think Events, Not Objects!
Nano revolutionizes microservice development by eliminating the need for complex object hierarchies. Instead of creating Controllers, Services, and Repositories, you simply listen to events and react with static methods.
How It Works:
- Events Flow Through Everything: HTTP requests, database operations, errors - all are events
- Static Listeners React: Your business logic lives in static methods that subscribe to events
- Services Are Universal Connectors: Services handle external integrations (HTTP, databases, queues) - no business logic
- TypeMap Handles Data: Automatic type conversion for JSON, XML, and any data format
- Global Error Handling: Subscribe to error events for centralized error management
Example - Traditional vs Nano:
// ❌ Traditional Spring Boot
@RestController
public class UserController {
@Autowired private UserService userService;
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody UserDto dto) {
return ResponseEntity.ok(userService.createUser(dto));
}
}
// ✅ Nano - Static Event Handling
public class UserController {
public static void handleCreateUser(Event<HttpObject, HttpObject> event) {
event.payloadOpt()
.filter(HttpObject::isMethodPost)
.filter(req -> req.pathMatch("/users"))
.ifPresent(req -> {
// Business logic here - no objects needed!
event.context().sendEvent(EVENT_CREATE_USER, req.bodyAsJson().asMap());
req.createResponse().statusCode(200).respond(event);
});
}
}
📚 Components
All you need to know are few classes: Context, Events, Schedulers, Services
flowchart LR
nano(((Nano))) --> context[Context]
context --> ch[Channels]
nano --> services[Services]
context --> schedulers[Schedulers]
ch -->|sendEvent| listeners[Listeners]
services -->|ack/reply| responses[(Responses)]
listeners -->|ack/reply| responses[(Responses)]
style nano fill:#90CAF9,stroke:#1565C0,stroke-width:1px,color:#1A237E,rx:2%,ry:2%
style context fill:#90CAF9,stroke:#1565C0,stroke-width:1px,color:#1A237E,rx:2%,ry:2%
style ch fill:#90CAF9,stroke:#1565C0,stroke-width:1px,color:#1A237E,rx:2%,ry:2%
style listeners fill:#90CAF9,stroke:#1565C0,stroke-width:1px,color:#1A237E,rx:2%,ry:2%
style responses fill:#90CAF9,stroke:#1565C0,stroke-width:1px,color:#1A237E,rx:2%,ry:2%
style services fill:#90CAF9,stroke:#1565C0,stroke-width:1px,color:#1A237E,rx:2%,ry:2%
style schedulers fill:#90CAF9,stroke:#1565C0,stroke-width:1px,color:#1A237E,rx:2%,ry:2%
⚙️ Mechanics
The Nano Way: Static, Event-Driven, and Stateless
Nano's architecture is fundamentally different from traditional frameworks:
🏗️ Architecture Principles:
- Static Methods for Business Logic: No need for service objects - just static methods that react to events
- Services as Universal Connectors: Services handle external integrations only (databases, HTTP, queues) - no business logic
- Event-Driven Communication: Everything flows through events - HTTP requests, database operations, errors
- TypeMap for Data Handling: Automatic type conversion and transformation for JSON, XML, and any data format
- Global Error Handling: Subscribe to error events for centralized error management
- Built-in Logging: Context automatically provides logging - no setup required
🔄 How It All Connects:
- HTTP Request → Event → Static Listener → Business Logic
- Database Operation → Event → Database Service → Response Event
- Error Occurs → Error Event → Global Error Handler → Error Response
- Error Handling
- Registers (ConfigRegister, TypeConversionRegister, LogFormatRegister, EventChannelRegister)
- Integrations (🌱 Spring Boot, 🧑🚀 Micronaut, 🐸 Quarkus)
- Code Examples
📚 Getting Started
Maven example
<dependency>
<groupId>org.nanonative</groupId>
<artifactId>nano</artifactId>
<version>2025.1.0</version>
</dependency>
Gradle example
dependencies {
implementation 'org.nanonative:nano:2025.1.0'
}
Quick Start
New to Nano? Start with our Quick Start Guide for a complete walkthrough.
Want to see examples? Check out our Examples & Common Solutions with real-world patterns.
Building a web API? Check out our HTTP Service Guide with real-world examples.
Need architecture guidance? See our Core Concepts for best practices.
Looking for documentation? Check out our comprehensive guides in the docs directory.
Simple Example
Here's a basic HTTP server with Nano showing the event-driven approach:
public static void main(final String[] args) {
// Start Nano with HttpServer
final Nano app = new Nano(args, new HttpServer());
// Static method handles GET /hello - no @Controller needed!
app.subscribeEvent(EVENT_HTTP_REQUEST, MyController::handleHello);
// Global error handling - even errors are events!
app.subscribeError(EVENT_HTTP_REQUEST, MyController::handleError);
}
public class MyController {
// Static method for business logic - no objects, no state!
public static void handleHello(Event<HttpObject, HttpObject> event) {
event.payloadOpt()
.filter(HttpObject::isMethodGet)
.filter(request -> request.pathMatch("/hello"))
.ifPresent(request -> request.createResponse()
.body(Map.of("Hello", System.getProperty("user.name")))
.respond(event));
}
// Global error handling - subscribe to error events!
public static void handleError(Event<?, ?> event) {
event.payloadAck()
.createResponse()
.body("Internal Server Error [" + event.error().getMessage() + "]")
.statusCode(500)
.respond(event);
}
}
Real-World Examples
- Complete Examples: See our Examples & Common Solutions for comprehensive patterns
- User Management: Check out our Simple User API Example
- More Examples: Browse all examples in src/test/java/org/nanonative/nano/examples/
🔨 Build Nano
add the native-image profile to your pom.xml and run mvn package -Pnative-image
<profiles>
<!-- NATIVE COMPILATION -->
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>21.2.0</version>
<configuration>
<imageName>ExampleApp</imageName>
<mainClass>de.yuna.berlin.nativeapp.helper.ExampleApp</mainClass>
<buildArgs>
<!-- Reduces the image size - Ensures the native image doesn't include the JVM as a fallback option -->
<buildArg>--no-fallback</buildArg>
<!-- Disables the use of the GraalVM compilation server -->
<buildArg>--no-server</buildArg>
<!-- Improve startup time - Initialize classes at build time rather than at runtime -->
<buildArg>--initialize-at-build-time</buildArg>
<!-- Include all files under /resources -->
<buildArg>-H:IncludeResources=resources/config/.*</buildArg>
</buildArgs>
</configuration>
<executions>
