eXXcellent solutions GmbH

Schlank in den Service:
Der große Java-
Microframework-Test!

2018-03-13
Benjamin Schmid
Technology Advisor
? for help
Schlank in den Service: Der große Java-Microframework-Test! by Benjamin Schmid is licensed under a Creative Commons License (BY-NC-SA/4.0) . Creative Commons Lizenzvertrag
http://microjs.com/

Microservice Architektur

Overview Microservice Architektur

Entscheidend: Geringe Komplexität/Aufwand sowie geringe Größe

Ben's Avatar

Java Microframeworks:

„Elegante, flexible auf ein Thema fokussierte Rahmenwerke für eine Problemstellung (in der Regel Webanwendungen) mit dem Ziel einer schlankere Source- und/oder Bytecode-Linie“

Die Kandidaten

Spark Java
Ninja
Jodd
Ratpack
Spring 5
Einkaufsliste Microservice Architektur Microservice Architektur

Eleganter Zündfunken

Datenblatt

Sinatra-inspiriert
Minimaler Boilerplate
Ausdrucksstärke
Standalone (Jetty 9)

Java 8 & Kotlin

zusätzliche Spark-DSL unter Kotlin

2.7.1 (Nov. 2017), APL2
Start: ~Feb. 2013
http://sparkjava.com/

Quick-Start #1 build.gradle

apply plugin: 'java'
apply plugin: 'application'

repositories {
    jcenter()
}

dependencies {
    mainClassName = 'bentolor.HelloSpark'
    compile         'com.sparkjava:spark-core:2.5'
}

Quick-Start #2 build.gradle

package bentolor.sparkjava;

import static spark.Spark.*;

public class HelloWorld {

    public static void main(String[] args) {
        get("/hello", "text/plain", (req, res) -> "Hello World!");

        get("/greet/:name", "text/plain",
            (req, res) -> "Hello "+req.params("name")+"!");
    }

}
## Kern-Konzepte 1. *DSL* zur Definition von *Routen* `methode(pfad, type, callback)` 2. zusätzlich: Filter & Transformer 3. optional: Template Engine Support *That's it!*
## Templating ```java get("/greet/:name", "text/html", (req, res) -> { String name = req.params("name"); Map<String, String> values = new HashMap<>(); values.put("name", name != null ? name : "Anon"); return new JadeTemplateEngine().render( new ModelAndView(values, "greet") ); }); ``` ##### z.B. mit Jade4j Template `greet.jade` ``` doctype html html body h1 Welcome to my Microservice p Dear #{name}, welcome to my web-based Microservice endpoint ``` Alternativ: Velocity, Freemarker, Mustache, Handlebars, Thymeleaf & 5 weitere …

Spark Java

Elegant & Minimalistisch
Sinatra-inspiriert. Deklarativ.


Strukturierung: Routen, Filter, Transformer,
optional Template Engines – That's it!


Jetty 9, slf4j

Sehr schnell erste Ergebnisse.
Macht Spaß in der Verwendung!

Minimalismus bedeutet aber auch:
Zergliederung, Fehlerbehandlung Antwortseiten in Entwicklerverantwortung

Dokumentation könnte umfassender & gründlicher sein. Code aber gut verständlich & debugbar

      |
    | 
2,7
4,9
81
39
Einkaufsliste Microservice Architektur Microservice Architektur
### Model: POJOs mit Lombok ```java /** A shopping list for groceries. */ @Data @NoArgsConstructor @AllArgsConstructor public class GroceryList implements ModelElement { private UUID id; private LocalDate date; private String comment; private boolean settled; private Item[] shoppingItems; } ```
#### ... oder mit Kotlin ```kotlin data class GroceryList(val id: UUID, var date: LocalDate, var comment: String?, var isSettled: Boolean, var shoppingItems: List<Item>) ```

Spion mit Konventionen

Datenblatt

CoC
MVC
Stateless

Full-Stack

Hibernate, Guice, Jackson,
Flyway, Ehcache, …

Nicht wirklich „Micro“

6.2.2 (Jan. 2018), APL2
Start: Juli 2012
http://www.ninjaframework.org/

Website screenshot

Convention-over-Configuration (CoC)

##### Fixe Paket/Ordnerstruktur & MVC Pattern * *Models:* Im Paket `model` * *Routendefinitionen:* In Klasse `conf.Routes` * *Controller:* Im Paket `controller` * *Views (MVC):* Freemarker-Template `views/CONTROLLER/METHOD.ftl.html` ##### Konfiguration * *Applikation:* `conf/application.conf` * *Guice:* `conf/Module.java` * *I18N:* `conf/messages_XX.properties`
##### Binding * *Dependencies:* Guice CDI. * *Input Parsing:* via `Content-Type` * *Bean Validation:* Automatisch bei passenden Controller-Methodensignaturen ##### Persistenz * *ORM:* JPA und Guice Persist * *Change Management:* mittels Flyway * *I18N:* `conf/messages_XX.properties`
## Ninja – Controller ```java package controllers; @Singleton public class GroceryListController { @Inject private Repository repository; public Result index() { return html(); } public Result createGroceryList(GroceryList newList) { return ok().render(repository.createList(newList)); } public Result getGroceryList(@PathParam("id") String id) { Optional<GroceryList> match = repository.getList(id); return match.isPresent() ? json().render(match.get()); : notFound().render(new Message(id + " not found")); } ```

← Immer: controllers



← Guice Dependency Injection

← Immer: Result
/GroceryListController/index.ftl.html


← Request Body
     geparsed via Content-Type


← URL Parameter


← Utility Methoden für Result

Ninja

Full Stack & MVC
Umfassende Konventionen
Play!-inspired Client-side Session

daher einfache Skalierbarkeit

Jetty 9, Guice, Guava, Logback, Jackson,
Woodstox, 6x commons-*, joda-time, ehcache,
memcached, flyway, freemarker, hibernate, prettytime, slf4j

Sehr umfassend.
Kombiniert viele etablierte Frameworks.

Architektur sehr stark vorgegeben. Wenig Flexibilität / Modularität

Umgehen von Detailproblemen durch monolithischen Ansatz teils schwer

Von Verbreitung eher Nische gegenüber Dropwizard/Spring Boot/Play!

      |
    |
  | 
25
4,8
82
127
Einkaufsliste Microservice Architektur Microservice Architektur
GUI in 64 Zeilen mit ng-admin : https://github.com/marmelab/ng-admin

ng-admin (1)


var myApp = angular.module('myApp', ['ng-admin']);
myApp.config(['NgAdminConfigurationProvider',
                function (NgAdminConfigurationProvider) {

    var nga = NgAdminConfigurationProvider;
    var admin = nga.application('My Grocery Lists');

    var configureFields = function (view) {
        return view.fields([
            nga.field('id').editable(false),
            nga.field('comment'),
            nga.field('settled', 'boolean'),
            nga.field('date', 'date'),
            nga.field('shoppingItems', 'embedded_list')
                .targetFields([
                    nga.field('quantity', 'number'),
                    nga.field('unit', 'choice').choices([
                        {value: 'pcs', label: 'pieces'},
                        {value: 'kg', label: 'kilogram'},
                        {value: 'g', label: 'gram'},
                        {value: 'l', label: 'litre'}
                    ]),
                    nga.field('name', 'string')
                ])
        ]);
    };

ng-admin (2)


    // lists entity
    var list = nga.entity('list');

    list.listView()
        .fields([
            nga.field('comment'),
            nga.field('settled', 'boolean'),
            nga.field('date', 'date')])
        .listActions(['show', 'edit', 'delete'])
        .title("My shopping lists");

    configureFields(list.editionView())
        .title('Edit "{{ entry.values.comment }}"')
        .actions(['show', 'list', 'delete']);
    configureFields(list.showView())
        .title('View "{{ entry.values.comment }}"');
    configureFields(list.creationView());

    admin.addEntity(list);

    nga.configure(admin);
}]);

Floppytaugliches Baukastensystem

Datenblatt

Komplettes
Baukastensystem

an Microframeworks & Tools !

IoC, AOP,
Bean-Validation,
SQL/OR-Mapper,
HTML Parser, …

Einzeln einbindbar
Alles zusammen: 1,7MB

4.2.0 (März 2018), APL2
Start: 2009
https://jodd.org/

Website screenshot

Jodd Komponenten (Auszug)


Jodd components
Und noch einiges mehr in nur 1,7MB!
(Validierung, HTTP, Utils, CSS, …)

### Jodd – Madvoc: Controller ```java @MadvocAction public class ListAction { @In @Scope(ScopeType.SERVLET) HttpServletRequest request; @RestAction("/list/{id}") @GET public GroceryList getList(@In("id") String id) { return GroceryService.get().getGroceryList(id); } … } ``` * Injection von Parametern und Servlet-Werten via `jodd-petite` IoC * Automatische Konvertierung/Serialisierung via `jodd-json` * `Action` optional – dann URL-Pfade via Konventionen
### Jodd – Limitationen in der Praxis *Problem:* JSON Serialisierung/Deserialisierung weitestgehend fix.
Format unserer `LocalDate` Instanzen nicht passend. *Workaround:* (De-)Serialisierung selbst in die Hand nehmen: ```java @Action("/list") @POST public MyJsonResult createList(@In @Scope(SERVLET) String jsonString) { return GroceryService.get().createGroceryList(jsonString)); } ``` ```java @RenderWith(MyJsonResultRenderer.class) public class JsonResult { private final Object model; private final int status; … ```

Jodd (Madvoc)

Ausgeprägt
Leichtgewichtig / Modular / KISS

Trotz 1,7MB weit mehr als nur Web-Framework. Geringe Verbreitung.


nichts!

Große Breite
bei geringer Größe & Einfachheit.

Microframework at it's best!

Öfters Breaking changes. Engagierter Maintainer – aktuell aber in der Sinnkrise

In komplexeren Fällen ggf. Rückgriff auf umfassendere Lösungen notwendig

 |
     |
|
4,9
4,9
81
45

Reaktiver Entertainer

Datenblatt

Reaktiv, Asynchron
Event-basiert,
stark typisiert

hohe Ähnlichkeit mit Vert.X

Basis:
Java 8, Netty & Promises

Zahlreiche Module:
Guice, Groovy, Jackson, Redis, Hystrix, Templating

1.5.3 (März 2018), APL2
Start: 2012
https://ratpack.io/

Website screenshot
### Ratpack – Handler Factory (statische Routen) ```java public static void main(String... args) throws Exception { ObjectMapper objectMapper = ModelSerializer.buildConfiguredObjectMapper(); GroceryService service = new GroceryService(); RatpackServer.start(server -> server .serverConfig(configBldr -> configBldr.baseDir(BaseDir.find()).development(true).port(8080)) .registryOf(r -> r.add(objectMapper)) .handlers(chain -> chain // 1. URL: /list .prefix("list", nested -> { nested.path(ctx -> ctx.byMethod(method -> { // 2. POST ---> Delegation an unseren vorherigen Handler! --- method.post(() -> service.createGroceryList(ctx)); method.get(() -> service.getAllGroceryLists(ctx)); })); nested.path(":id", ctx -> ctx.byMethod(method -> { method.put(() -> service.updateGroceryList(ctx)); method.get(() -> service.getGroceryList(ctx)); method.delete(() -> service.deleteGroceryList(ctx)); })); }) .files(f -> f.indexFiles("index.html")) ) ); } ``` **Verschachtelte Entscheidungslogik:** Recht verwirrend und wenig intuitiv.

Ratpack

Asynchron
non-blocking
reaktiv


Netty, Jackson, Javassist, SnakeYaml, slf4j

Interessanter Ansatz & Konzept

Problematisch: viele Lücken in der Dokumentation

Asynchronen Programmierung in Kombination mit vielen Lambda-Ausdrücke begrenzt intuitiv.

Komplexität vs. Gewinn?

     |
 |
|
9,5
4,5
87
31

Start für den Platzhirsch

Datenblatt

CoC für Spring
= Full-stack
Auswahl an „Starters"
Maven-zentrisch

Ziele
Möglichst viel Autokonfiguration
Standalone Apps
„Opinionated“ Vorlagen

Hier gewählt
Spring 5 mit WebFlux (reaktiv)

2.0.0 (März 2018), APL2
https://projects.spring.io/

Website screenshot

Spring Boot

Mainstream. Full-stack.

Kein "Micro" – eher ganze Landschaft

Spring Boot, Spring Web/Beans/Core/JCL,
WebFlux, Reactor/ReactiveStreams, Netty
Hibernate Validator, slf4j, SnakeYaml, Logback, Jackson

Viel Automagie.
Hohe Komplexität & Lernkurve

Detailanforderungen können schnell Expertenwissen erfordern

Höchstes Niveau an
Support & Verbreitung

Umfangreiche Dokumentation

       |
   |
      |
16
4,1
98
149

Benchmark Crunchdown
(Bytecode Size)

All-in-one JAR

Benchmark Crunchdown
(Request Duration)

$ ab -n 400000 -c 400 "http://localhost:8080/list?_page=1&_…"
Spring 5 Benchmark Plot
Pippo Benchmark Plot

Micro macht Spaß!

Risiken:
Traktion, Stabilität, geringe Verbreitung

Gegenstrategie:
Fokus: Micro & gute Verständlichkeit; Bereitschaft, Bugs ggf. selbst zu lösen

Materialien, Links & Weiterführendes

###### Microframeworks * [sparkjava.com](http://sparkjava.com/) * [ninjaframework.org](http://www.ninjaframework.org/) * [jodd.org](https://jodd.org/) * [ratpack.io](https://ratpack.io/) --- * [pippo.ro](http://www.pippo.ro/) * [jooby.org](http://jooby.org/) * [Javalite.io](http://javalite.io/) * [bootique.io](https://bootique.io/) --- * [REST Service w/o any Framework](https://www.stubbornjava.com/posts/lightweight-embedded-java-rest-server-without-a-framework) only with [Undertow container](http://undertow.io/)
###### Efficency * [projectlombok.org](https://projectlombok.org/) ###### Sonstiges * Präsentation via [reveal.js](https://revealjs.com) * Photos via [unsplash.com](https://unsplash.com/) * Charts via [chartist.js](https://gionkunz.github.io/chartist-js/)
###### Vom Autor * [*@bentolor*](http://twittter.com/bentolor) * [*Java++*: Effiziente & sichere JVM-Entwicklung mit Kotlin](https://bentolor.github.io/kotlin-talk/) * [`idea-cli-inspector`](https://github.com/bentolor/idea-cli-inspector)