Technical Note / Article

Migrating an IoT Audit Engine into a Java 21 + Spring Boot Platform

Author: Thomas Bonderup Published: Reading time: 10 min read

This migration is not a full Scala-to-Java rewrite. The Scala/ZIO execution engine stays in place while a Java 21 + Spring Boot platform is built around it for control-plane workflows, integration, and long-term platform fit.

What this note helps clarify

Pressure shows up early

These articles focus on the points where reliability, architecture clarity, or secure delivery start to break under real-world pressure.

The technical question should stay concrete

Use the patterns here to narrow which system boundaries, delivery risks, or operating assumptions need evidence first.

The result should be reusable

The goal is to leave behind architecture notes, implementation patterns, or questions that another engineer can use quickly.

IoT Audit Migration Reliability
This migration is not a full Scala-to-Java rewrite. The Scala/ZIO execution engine stays in place while a Java 21 + Spring Boot platform is built around it for control-plane workflows, integration, and long-term platform fit.

Key takeaways

  • Not every migration should be a full rewrite; sometimes the right move is to build a new platform around a proven core.
  • Keep the execution engine where it already works and move control-plane concerns into the ecosystem that best fits integration and team needs.
  • Use clear boundaries, contracts, and phased cutovers so platform evolution does not break the system that already delivers value.

Context: Why migrate (market fit, hiring signal, operational consistency)

This post is a follow-up to Designing an Evidence-Based Audit Engine for IoT Security & Reliability, where I described the original Scala + ZIO design of the audit engine.

This post still deals with migration toward Java 21 + Spring Boot, but there is an important clarification:

this is not a complete rewrite of the audit engine from Scala into Java.

The more accurate description is that I am starting to migrate the overall system into a Java-based platform for control-plane related work, while the existing Scala/ZIO execution engine continues to own the execution-focused part of the system.

The motivation for this migration is mainly technology and platform fit. In Denmark, Scala is still relatively niche, while Java and C# dominate backend hiring and are the default choice in many companies. Building the platform layer in Java improves:

  • Hiring signal by aligning the surrounding platform with more common backend stacks
  • Operational consistency through more standard platform conventions, tooling, and team familiarity
  • Maintainability by lowering the barrier for contributors and future maintainers
  • Control-plane fit for APIs, orchestration, lifecycle management, integrations, and administrative workflows

So the interesting change is not “translate everything from Scala to Java.” It is: keep the proven engine, and build the platform around it in Java.

My Java and Scala background

My Java experience goes back more than a decade, across both professional and academic projects, before I later leaned more heavily into Scala for distributed systems and functional programming.

2013-2016: Early Java projects (OOP foundations)

During my first computer science degree, I focused heavily on object-oriented programming in Java and built several end-to-end projects, including:

  • a beehive owner management system
  • a flight route planner system

2018-2020: Java backend + distributed systems foundations (BSc CS)

During my bachelor’s in computer science, I worked with more advanced backend concepts and Java for production-style environments:

  • Spring Boot and scalable service platform patterns
  • REST APIs, integration patterns, microservice-oriented thinking, and container deployments with Docker and Kubernetes
  • a Java + Spring Boot + Apache Kafka bachelor project for anomaly detection on vehicle sensor data

2020-2022: Java security + advanced streaming with Kafka (MSc)

During my master’s, I used Java across security, backend development, and systems projects:

  • a file encryption application using Java Bouncy Castle
  • a Spring Boot application related to digital misinformation
  • a Virtual Power Plant prototype for my master’s thesis using Kafka for streaming weather, energy, and market data

Transition to Scala: Akka -> data platforms -> FP

During my master’s thesis, I began transitioning from Java to Scala, initially to leverage the actor model and the Akka ecosystem. After graduating, I worked full-time with Scala in data engineering and spent time going deeper into Cats and ZIO.

That means this migration is not about learning Java from scratch. It is about deciding where Java is the better fit in the overall architecture, while preserving the strengths of the Scala/ZIO design where they still matter.

What is actually changing

At first glance, the project title can sound like a classic Scala-to-Java rewrite story. That is not really what is happening.

The existing audit engine already solves an important problem:

  • run probes
  • collect evidence
  • evaluate rules
  • produce findings

That core is valuable. Rewriting it end-to-end just to unify language would introduce risk without necessarily creating equivalent value.

The more accurate migration shape is:

  • keep the existing Scala/ZIO execution engine for audit execution and rule evaluation
  • build a Java 21 + Spring Boot control plane around it
  • move platform-facing concerns into Java first
  • evolve the whole system through clearer boundaries instead of a big-bang rewrite

This is much closer to how migration projects often work in reality: there is already a useful system, but it needs a platform around it that better fits integration needs, team composition, deployment conventions, and long-term maintenance.

What stays in Scala, and what moves into Java

The important distinction here is between the execution engine and the control plane.

The execution engine is the part that:

  • triggers or coordinates audit execution
  • invokes the evidence probes
  • collects raw outputs
  • evaluates rules
  • turns evidence into findings

That part already aligns well with the original Scala/ZIO design, especially around explicit effects, orchestration, and deterministic flow.

The Java platform is a better fit for concerns such as:

  • external APIs and control-plane endpoints
  • user-facing workflow orchestration
  • asset and tenant management
  • scheduling and audit lifecycle coordination
  • reporting, integrations, and administrative surfaces
  • the broader product shell around the engine

The first public implementation slice of that direction is the Certificate Control Plane API, a Java 21 + Spring Boot backend for tenant-aware certificate inventory, asset relationships, renewal workflow tracking, and audit-platform operations. The code is available in the public certificate-control-plane-api GitHub repository.

That split matters because it turns the migration into a boundary design problem instead of a code translation exercise.

Principle: treat migration as a reliability project

A migration is not just rewriting code in another language. It changes the system’s failure modes: configuration, threading and concurrency, timeouts, serialization, database drivers and pooling, dependency injection boundaries, deployments, and observability defaults.

Even if the intended business behavior stays the same, the operational behavior often does not.

That is why I treat this as a reliability project first and a language migration second, very much in the spirit of making safety nets before making large changes1.

In this case, the core challenge is not “can I implement this in Java?” The core challenge is: can I evolve the platform around an existing engine without breaking trust in the system?

Migration approach

My approach is incremental:

  • stabilize the current engine boundary so the Java side can orchestrate it through an explicit contract
  • move control-plane responsibilities first rather than re-implementing engine logic prematurely
  • preserve execution semantics so evidence, findings, and audit behavior stay trustworthy while the surrounding platform changes
  • revisit deeper migration later only if there is a strong reason to move engine slices as well

The exact transport between the Java platform and the Scala engine can vary depending on the final operating model: internal HTTP API, messaging, or command-oriented integration. The important part is the architectural rule:

the Java side should orchestrate the engine through a clear contract, not by translating all engine behavior up front.

That is where contract tests and comparison tests matter more than language ideology, and where an incremental Strangler Fig style of evolution is more useful than a big rewrite promise2.

Why this framing is more useful than “rewrite from Scala to Java”

I think this is a more honest description of the project.

Real systems are rarely replaced all at once. More often, they are surrounded, stabilized, integrated, and gradually re-shaped. The valuable engineering decision is knowing what to preserve and what to move.

In this case, the important decision is:

  • preserve the part that already expresses the audit domain well
  • move the parts that need stronger platform fit into Java
  • let architecture boundaries do the work that a risky rewrite would otherwise try to do

That is also much closer to the kind of modernization work many teams actually face: not greenfield replacement, but building a better platform around an existing system without losing the capabilities that made it worth keeping.

Closing thought

The interesting part of this migration is not that Java 21 + Spring Boot is replacing Scala + ZIO across the board.

The interesting part is that an existing audit engine now needs a broader platform around it, and Java is the right place for much of that control-plane work. The engine remains an execution-focused subsystem. The platform around it becomes easier to integrate, staff, operate, and evolve.

That is the migration I am actually building.

For the concrete backend implementation that now exists around this idea, see the Certificate Control Plane API portfolio entry or inspect the public certificate-control-plane-api repository.

References

Footnotes

  1. Working Effectively With Legacy Code by Michael C. Feathers

  2. Strangler Fig pattern: https://martinfowler.com/bliki/StranglerFigApplication.html

Thomas Bonderup

Thomas Bonderup

Senior Software Engineer

Specializes in IoT architecture, distributed systems, reliability and observability, edge-to-cloud delivery.

Notes from building systems

If this post overlaps with something you're building, I'd be happy to compare notes on the engineering.

Some posts come from practical engineering work, some from deep technical exploration, and some from earlier builder projects. The common thread is reusable thinking you can adapt to your own system.

Technical scope: IoT architecture, distributed systems, reliability and observability, edge-to-cloud delivery.

Continue into related work or get in touch

If this note overlaps with the systems you care about, continue into related projects, the CV, or the contact page for hiring and technical follow-up.

Related content

Get in touch about this note

Use this for hiring conversations, technical discussion, or peer follow-up.

Prefer direct contact? Call +45 22 39 34 91 or email tb@tbcoding.dk.

Best for hiring conversations, technical discussion, and thoughtful follow-up on published work.

Typical response time: same business day.