Spring Boot und Kotlin, ein kurzer Erfahrungsbericht
Hintergrundinformationen zum Projekt
In diesem Projekt haben wir eine individuelle Software-Lösung implementiert, um den Produktionsprozess unseres Kunden zu überwachen. Hierzu werden Laufkarten gescannt und die Zeiten, wann eine Laufkarte in einer Abteilung ein- und ausgebucht wird, mit unserer Lösung protokolliert. Auf Basis dieser Daten werden Dashboards und wöchentliche Reports erstellt, mit denen die Produktion gesteuert wird.
Technisch basiert die Software auf Spring Boot für das Back-End und VueJs für das Front-End. Die Lösung wird in der Google Cloud (App Engine und Cloud SQL for MySQL) betrieben.
Warum eine neue Programmiersprache wie Kotlin?
Neben Spring Boot und Java entwickeln wir unsere Projekte hauptsächlich mit Groovy und Grails. Die Sprache Groovy ermöglicht es im Vergleich zu Java, einen kompakteren und damit übersichtlicheren Code zu schreiben. Man denke nur an das Handling von NullPointerExceptions (NPE) oder den ganzen Boilerplate-Code, den man beispielsweise für die Getter und Setter schreiben muss. Bei Java setzen wir dann beispielsweise das Projekt Lombok ein, um den Boilerplate-Code für die Getter und Setter zu vermeiden. Dafür werden dann jedoch Annotationen benötigt, die den Code nicht übersichtlicher machen.
Ab Java 14 wird die Getter/Setter-Thematik mit dem speziellen Klassen Typ Record endlich behoben. Leider wird Java 14 noch nicht in der Google App Engine unterstützt. So kamen wir zu dem Schluss, dass es sinnvoll wäre, Kotlin auszuprobieren. Hier ein paar Features – auch wenn es bei Kotlin noch viel mehr Features gibt, die uns die Arbeit im Vergleich zu Java schon oft immens erleichtert haben:
- Einfachere Refactorings – oben erwähnte ich meine Begeisterung gegenüber der Sprache Groovy. Natürlich gibt es bei Groovy nicht nur Vorteile. Ein Vorteil, den man auch als Nachteil sehen kann ist, dass Groovy im Vergleich zu Java eine dynamische Programmiersprache ist. Demzufolge werden beispielsweise GORM-Finder-Methoden dynamisch zur Laufzeit gebunden. Dies hat den Vorteil gegenüber Spring Boot, dass man keine Repository-Klassen pflegen muss, aber den Nachteil, dass Fehler im Vergleich zu Java erst während der Laufzeit auftreten. Aus diesem Grund haben wir uns bei Groovy und Grails angewöhnt, sehr viele Test-Cases zu schreiben. Dies gibt dann beim Refactoring mehr Sicherheit. Da Kotlin eine statische Programmiersprache ist, muss man für das Refactoring nicht zwingend Tests schreiben, aber natürlich sind auch bei Kotlin Test-Cases sinnvoll.
- Datenstrukturen mit mehr Unterstützung – in unserem Projekt müssen wir die Ergebnisse der SQL-Abfragen nochmals nachbearbeiten, da diese teilweise zu komplex (enthalten bedingte Anweisungen und Verzweigungen) für SQL sind. Aber auch die mächtigen Listen-Methoden wie groupBy, die mit einem Tupel nach Wochentag und zusätzlich einer Auftragsnummer suchen können, sind uns ans Herz gewachsen.
- Getter/Setter – wie oben erwähnt, entfällt bei Kotlin der ganze Boilerplate-Code der in Java für die Getter/Setter-Methoden geschrieben werden muss. Des Weiteren kann man mit Data-Klassen, die dazu gedacht sind nur Daten zu speichern, nun in einer Zeile schreiben, wofür man in Java sehr viele Code-Zeilen benötigt hat.
- Besseres String-Handling – das oben geschilderte Projekt verfügt über eine große Anzahl von Dashboards. Die Datenbankabfragen für die Dashboards machen wir inzwischen nicht mehr mit JPA sondern direkt mit SQL. Dies hat für uns den Vorteil, dass wir komplexe Dashbord-Abfragen, die schwer in JPA abbildbar sind, direkt in einem SQL-Query-Tool erstellen. Damit können wir die Ausführung exakt festlegen und sind nicht davon abhängig, wie Hibernate diese Queries dann letztendlich abbildet. Bei den Raw-Strings (“““) von Kotlin, die wir für die SQL-Queries benutzen, konnten wir nun sehen, dass diese viel einfacher und leserlicher mit Kotlin abzubilden sind.
- NullPointerException(NPE)-Handling – hier arbeitet Kotlin analog zu Groovy mit dem Safe-Call-Operator ?. . Dieser ermöglicht es, einen sehr kompakten NPE-sicheren Code wie val res = person?.country?.code zu schreiben. Es gibt auch noch den Unsafe-Call-Operator !!. , der eine NPE wirft. In Java wird hierfür Optional() benutzt, was den Code meines Erachtens jedoch schnell sehr unübersichtlich macht.
Wie mischt man Java mit Kotlin?
In diesem Projekt hatten wir zunächst mit Java innerhalb von Spring Boot begonnen, und dann später Kotlin nachgezogen. Hierbei konnten wir bis auf die JPA-Entitäten den Code beliebig mischen, sprich Java-Code ruft Kotlin auf und umgekehrt. Bei den Entitäten, die wir anfänglich in Java implementiert hatten, hat dies nicht funktioniert, da wir das Lombok-Framework benutzt haben, um uns die Erstellung der Getter und Setter zu ersparen. Leider kann Kotlin die Getter und Setter nicht sehen, die Lombok on-the-fly erzeugt. Wir haben uns dagegen entschieden, das kapt (Kotlin Annotation Processing Tool) Plugin in Intellij zu benutzen, weil wir keine weiteren Abhängigkeiten wollten. Aus diesem Grund haben wir uns schließlich dafür entschieden, dann alle JPA-Entitäten nach Kotlin zu migrieren. Mit dem Intellij-Assistenten ging das fast vollautomatisch von der Hand.
Fazit
Auch wenn Kotlin hauptsächlich für die mobile Entwicklung von Android Apps eingesetzt wird, kann es auch sehr gut für serverseitige Entwicklung eingesetzt werden. Hierbei zeigen sich die Vorteile gegenüber Java: ein übersichtlicherer und somit leichter zu verstehender Code entsteht.
Vergleicht man den Code von einer Spring Boot/Kotlin-Applikation mit einer Grails/Groovy-Applikation, so ist der Source Code von Groovy und Grails jedoch noch übersichtlicher, da dieser die ganzen Annotationen mit dem Ansatz „Convention over Configuration“, den sich Grails von Ruby on Rails abgeguckt hat, zum großen Teil überflüssig macht.