Recent Posts
Archives

Archive for the ‘en-US’ Category

PostHeaderIcon [DevoxxPL2019] Evaluating Micronaut Versus Spring Boot: A Framework Comparison

Lecturer

Vladimir Dejanović holds the position of senior director of B2C technology at PVH, overseeing fashion tech initiatives for brands like Tommy Hilfiger and Calvin Klein. As founder and leader of the Amsterdam Java User Group, he is a JavaOne Rockstar and CodeOne Star, frequently speaking on Java frameworks and architectures.

Abstract

This assessment contrasts Micronaut and Spring Boot, scrutinizing their capabilities in building Java applications. It outlines motivations for comparison, details a challenge involving repository implementations, and evaluates aspects like startup time, memory usage, and GraalVM compatibility. Through live demonstrations, it appraises design philosophies, performance metrics, and ecosystem maturity, while deliberating suitability for new versus existing projects.

Motivational Framework: Choosing Between Toolkits and Ecosystems

Selecting between library assemblages and comprehensive frameworks defines modern Java development. Vladimir articulates this dichotomy: libraries offer flexibility but demand integration, while frameworks like Spring Boot provide batteries-included convenience at potential runtime costs.

Context: Spring’s dominance stems from its power, yet expenses in reflection and startup manifest in cloud environments. Micronaut promises equivalent functionality sans drawbacks, leveraging compile-time processing.

Analytically, this addresses microservices’ needs for lightweight, fast-starting apps. Implications: frameworks accelerate prototyping, but overheads impact scaling; Micronaut’s approach could optimize resource utilization.

Challenge Setup and Implementation: Repository Patterns Examined

To compare, Vladimir devises a repository challenge: implement CRUD for persons with ratings from age and name. Spring Boot employs annotations for entities and repositories, extending CrudRepository for magic implementations.

Code:

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int age;
    private int rating;
    // getters/setters
}

@Repository
public interface PersonRepository extends CrudRepository<Person, Long> {}

Micronaut requires manual implementations, using @Repository and extending interfaces, coding CRUD in classes.

Analytically, Spring’s conciseness boosts productivity, while Micronaut’s explicitness aids understanding. Implications: Spring suits rapid development; Micronaut favors control in performance-critical scenarios.

Performance Benchmarks: Startup, Memory, and Native Compilation

Startup: Micronaut launches faster due to compile-time dependency injection, versus Spring’s runtime reflection. Memory: Micronaut consumes less, avoiding proxies.

GraalVM: Micronaut compiles natively out-of-box; Spring lacks seamless support.

Analytically, these metrics favor Micronaut in serverless or resource-constrained setups. Implications: reduced costs in cloud billing, faster cold starts enhancing user experience.

Ecosystem and Documentation: Readiness for Production

Micronaut’s ecosystem grows rapidly, supporting Kafka, GraphQL, gRPC, and service discovery. Documentation excels with guides and tutorials.

Spring Boot’s maturity offers vast integrations, but at higher overheads.

Analytically, both enable quick solutions, but Micronaut’s modernity appeals for greenfield projects. Implications: Micronaut suits innovation; retain Spring for legacy stability.

Concluding Evaluations: Strategic Framework Selection

Both excel in productivity, with Spring edging in simplicity, Micronaut in efficiency. Retain existing Spring; consider Micronaut for new endeavors.

Implications: informed choices optimize for context, balancing speed and scalability.

Links:

PostHeaderIcon [KotlinConf2019] Kotless: A Kotlin-Native Approach to Serverless with Vladislav Tankov

Serverless computing has revolutionized how applications are deployed and scaled, but it often comes with its own set of complexities, including managing deployment DSLs like Terraform or CloudFormation. Vladislav Tankov, then a Software Developer at JetBrains, introduced Kotless at KotlinConf 2019 as a Kotlin Serverless Framework designed to simplify this landscape. Kotless aims to eliminate the need for external deployment DSLs by allowing developers to define serverless applications—including REST APIs and event handling—directly within their Kotlin code using familiar annotations. The project can be found on GitHub at github.com/JetBrains/kotless.

Vladislav’s presentation provided an overview of the Kotless Client API, demonstrated its use with a simple example, and delved into the architecture and design concepts behind its code-to-deployment pipeline. The core promise of Kotless is to make serverless computations easily understandable for anyone familiar with event-based architectures, particularly those comfortable with JAX-RS-like annotations.

Simplifying Serverless Deployment with Kotlin Annotations

The primary innovation of Kotless, as highlighted by Vladislav Tankov, is its ability to interpret Kotlin code and annotations to automatically generate the necessary deployment configurations for cloud providers like AWS (initially). Instead of writing separate configuration files in YAML or other DSLs, developers can define their serverless functions, API gateways, permissions, and scheduled events using Kotlin annotations directly on their functions and classes.

For example, creating a REST API endpoint could be as simple as annotating a Kotlin function with @Get("/mypath"). Kotless then parses these annotations during the build process and generates the required infrastructure definitions, deploys the lambdas, and configures the API Gateway. This approach significantly reduces boilerplate and the cognitive load associated with learning and maintaining separate infrastructure-as-code tools. Vladislav emphasized that a developer only needs familiarity with these annotations to create and deploy a serverless REST API application.

Architecture and Code-to-Deployment Pipeline

Vladislav Tankov provided insights into the inner workings of Kotless, explaining its architecture and the pipeline that transforms Kotlin code into a deployed serverless application. This process generally involves:
1. Annotation Processing: During compilation, Kotless processes the special annotations in the Kotlin code to understand the desired serverless architecture (e.g., API routes, event triggers, scheduled tasks).
2. Terraform Generation (Initially): Kotless then generates the necessary infrastructure-as-code configurations (initially using Terraform as a backend for AWS) based on these processed annotations. This includes defining Lambda functions, API Gateway resources, IAM roles, and event source mappings.
3. Deployment: Kotless handles the deployment of these generated configurations and the application code to the target cloud provider.

He also touched upon optimizations built into Kotless, such as “outer warming” of lambdas to reduce cold starts and optimizing lambdas by size. This focus on performance and ease of use is central to Kotless’s philosophy. The framework aims to abstract away the underlying complexities of serverless platforms, allowing developers to concentrate on their application logic.

Future Directions and Multiplatform Aspirations

Looking ahead, Vladislav Tankov discussed the future roadmap for Kotless, including ambitious plans for supporting Kotlin Multiplatform Projects (MPP). This would allow developers to choose different runtimes for their lambdas—JVM, JavaScript, or even Kotlin/Native—depending on the task and performance requirements. Supporting JavaScript lambdas, for example, could open up compatibility with platforms like Google Cloud Platform more broadly, which at the time had better support for JavaScript runtimes than JVM for serverless functions.

Other planned enhancements included extended event handling for custom events on AWS and other platforms, and continued work on performance optimizations. The vision for Kotless was to provide a comprehensive and flexible serverless solution for Kotlin developers, empowering them to build efficient and scalable cloud-native applications with minimal friction. Vladislav encouraged attendees to try Kotless and contribute to its development, positioning it as a community-driven effort to improve the Kotlin serverless experience.

Links:

PostHeaderIcon [DevoxxPL2019] Pursuing Software Excellence: Satirical Reflections on Quality Assurance

Lecturer

Chet Haase, a comedy writer and former software engineer at Google specializing in Android, has authored several books on the platform’s history and development. With decades in Silicon Valley, he now focuses on humorous presentations and writings, drawing from his engineering background.

Abstract

This satirical examination critiques conventional quality pursuits in software, proposing absurd strategies to eliminate defects entirely. It defines quality through bug absence, introduces mock equations and metrics, and debunks testing myths via humor. Through exaggerated organizational restructurings and release tactics, it highlights methodologies’ absurdities, contextualizing within engineering cultures, and pondering satirical implications for productivity and perceptions.

Redefining Quality: Beyond Bugs and Toward Perfection

Quality in software often evokes rigorous processes, yet Chet humorously redefines it as absolute bug elimination, achievable only by ceasing code production. He parodies presentations with Greek-lettered equations, positing quality inversely proportional to defects—zero bugs yielding infinite quality.

Contextually, this lampoons metric obsessions, where charts depict declining bugs equating rising excellence. Analytically, it underscores defect inevitability in active development, as coding introduces errors.

Implications: satirizes pursuits of unattainable perfection, urging balanced approaches where quality stems from iterative improvements, not cessation.

Organizational Overhauls: Eliminating Defect Sources

Chet proposes radical restructurings: fire engineers, as they author bugs. Managers, attending meetings sans coding, produce none—hire more. Sales rebrands existing releases (e.g., 2.3 as 3.0), avoiding new defects.

Methodologically, this “bug equilibrium” maintains status quo, parodying stagnation fears. Analytically, highlights engineers’ value despite imperfections, as innovation requires risk.

Consequences: mocks efficiency quests ignoring human elements, implying true quality demands collaboration, not elimination.

Misconceptions and Practical Jests: Testing Fallacies Exposed

Common myths: testing ensures quality—Chet counters, tests merely verify existing quality, ineffective sans code. Quality as quantifiable—satirizes certificates as superficial validations.

Analytically, underscores qualitative aspects like usability over metrics. Implications: encourages holistic views, where quality encompasses user satisfaction beyond defect counts.

Satirical Ramifications: Lessons in Absurdity

Chet’s humor reveals engineering absurdities: over-reliance on processes stifles creativity. Implications: fosters self-reflection, promoting balanced methodologies valuing people over perfection.

In essence, quality eludes simplistic fixes, demanding nuanced, human-centered strategies.

Links:

PostHeaderIcon [DevoxxPL2019] Crafting Effective Automated Tests: Insights Beyond Conventional Wisdom

Lecturer

Jacek Milewski serves as a senior software developer at Circle K, where he focuses on backend Java development in domains like fuel retail and electric vehicles. As a trainer at Bottega IT Minds, he conducts sessions on domain-driven design and software architecture, drawing from his experience as a consultant, speaker, and mentor in the IT community.

Abstract

This analysis investigates approaches to automated unit and integration testing in modular applications, emphasizing practical techniques for ensuring business logic integrity. It explores test builders for entity construction, in-memory versus real repositories, and the role of test-driven development in maintaining quality. Through a live-coded example of rating calculations based on age and name length, it evaluates methodologies for edge case coverage, assertion strategies, and the balance between speed and thoroughness, while considering implications for development velocity and software reliability.

Establishing Test Foundations: From Basic Assertions to Modular Design

Automated testing forms the bedrock of reliable software, yet many practitioners grapple with adapting strategies to evolving ecosystems. Jacek commences by underscoring the perpetual relevance of testing, as technological advancements continually introduce new challenges. His methodology revolves around a simple yet comprehensive example: computing a person’s rating from age and name length, where ratings range from 0 to 100, with penalties for ages under 18 or over 65, and bonuses for longer names.

Initial tests focus on isolated units, such as a rating calculator class. Here, inputs are mocked or directly provided, verifying outputs against expectations. For instance, a test might instantiate a person with age 20 and name “John Doe,” asserting the rating equals age plus name length, capped at 100. This isolates logic, ensuring purity without external dependencies.

As complexity grows, modularization becomes key. Jacek advocates separating concerns: entities hold data, services compute logic, repositories persist state. Tests then target these layers individually, using builders to construct test data fluently. A PersonBuilder might chain methods like withAge(25).withName(“Alice”).build(), promoting readability and reuse.

Contextually, this stems from real-world projects at Circle K, where business rules like vehicle charging require verifiable implementations. Analytically, such isolation accelerates feedback loops, catching defects early. However, over-isolation risks missing integration issues, necessitating complementary tests.

Implications extend to team dynamics: standardized builders reduce onboarding time, fostering consistency. Yet, excessive abstraction can obscure intent, demanding balance.

Integrating Dependencies: Balancing Unit and Integration Testing

Transitioning to dependencies, Jacek differentiates unit tests—focusing on isolated behavior—from integration tests, verifying interactions. For persistence, in-memory repositories simulate databases, allowing rapid execution without external setups.

In the rating scenario, a service saves rated persons to a repository. Unit tests inject mock repositories, asserting save invocations and contents. Code might resemble:

PersonBuilder builder = new PersonBuilder();
Person person = builder.withAge(30).withName("Bob").build();
RatingService service = new RatingService(new InMemoryRepository());
service.calculateAndSave(person);
assertEquals(1, repository.size());
assertEquals(34, repository.get(0).getRating());

This confirms logic without I/O overhead.

For integration, swap to real repositories (e.g., JPA with H2), reusing test structures. Jacek copies unit tests, altering only the injected repository, ensuring end-to-end validation with minimal duplication.

Methodologically, this dual approach leverages TDD: write failing tests, implement minimally to pass, refactor safely. Failing tests validate coverage—green from inception might overlook assertions.

Analytically, in-memory speeds iterations, while real databases catch schema mismatches. Implications: enhanced confidence in deployments, though integration suites slow CI pipelines, suggesting selective execution.

Optimizing for Development Speed: Dispelling Myths on Testing Overhead

A prevalent myth posits testing impedes velocity, yet Jacek counters with empirical observations: initial setups invest time, but yield dividends in maintainability. Without tests, early features deploy swiftly, but regressions mount, stalling progress.

Contrastingly, test-first approaches start slower—configuring builders, mocks—but sustain pace, as refactors preserve functionality. In his experience, untested codebases accrue debt, while tested ones enable fearless enhancements.

Methodologically, focus on meaningful assertions: verify behaviors, not implementations. For empty repositories, assert isEmpty() post-setup, confirming state.

Analytically, coverage metrics mislead if superficial; aim for edge cases like invalid ages or names. Implications: teams adopting this outpace untested counterparts long-term, delivering quality sustainably.

Broader Ramifications: Testing as a Catalyst for Quality Delivery

Testing transcends verification, shaping designs toward modularity. Jacek’s Circle K tenure illustrates: robust tests facilitate microservices evolution, aligning with business agility in retail.

Yet, no universal formula exists; adapt to domains—unit for logic, integration for persistence. Implications: cultivates culture valuing prevention over remediation, elevating software craftsmanship.

In summation, these practices, honed through experience, empower developers to deliver verifiable value efficiently.

Links:

PostHeaderIcon [KotlinConf2019] Desktop Development with TornadoFX: Kotlinizing JavaFX with Liz Keogh

JavaFX, the successor to Swing for creating rich client applications in Java, offers a modern approach to desktop UI development with a cleaner separation of function and style. However, working directly with JavaFX can sometimes involve verbosity and untyped stylesheets. Liz Keogh, a renowned Lean and Agile consultant and a core member of the BDD community, presented a compelling alternative at KotlinConf 2019: TornadoFX. Her talk explored how TornadoFX, a Kotlin wrapper around JavaFX, simplifies desktop development with type-safe builders, stylesheets, and the syntactic sugar Kotlin developers appreciate. Liz Keogh’s consultancy work can often be found via lunivore.com.

TornadoFX aims to make JavaFX development more idiomatic and enjoyable for Kotlin developers. It leverages Kotlin’s powerful features to reduce boilerplate and introduce modern development patterns like dependency-injected MVC/MVP. The official website for the framework is tornadofx.io.

Simplifying JavaFX with Kotlin’s Elegance

Liz Keogh’s session highlighted how TornadoFX enhances the JavaFX experience. Key advantages include:
* Type-Safe Builders: Instead of manually instantiating and configuring UI components in JavaFX, TornadoFX provides type-safe builders. This allows for a more declarative and concise way to define UI layouts, reducing the chance of runtime errors and improving code readability.
* Type-Safe Stylesheets: JavaFX typically uses CSS for styling, which is not type-safe and can be cumbersome. TornadoFX introduces type-safe CSS, allowing styles to be defined directly in Kotlin code with autocompletion and compile-time checking. This makes styling more robust and easier to manage.
* Dependency Injection and Architectural Patterns: TornadoFX incorporates support for architectural patterns like Model-View-Controller (MVC) and Model-View-Presenter (MVP) with built-in dependency injection. This helps in structuring desktop applications in a clean, maintainable, and testable way.
* Kotlin’s Syntactic Sugar: The framework makes full use of Kotlin’s features, such as extension functions, lambdas, and DSL capabilities, to create a fluent and expressive API for building UIs.

Liz demonstrated these features through practical examples, showing how quickly developers can create sophisticated desktop applications with significantly less code compared to plain JavaFX.

Practical Application: Building a Desktop Game

To illustrate the power and ease of use of TornadoFX, Liz Keogh built a desktop version of the game “Don’t Get Mad!” (a variant of Ludo/Pachisi) live during her presentation. This hands-on approach allowed attendees to see TornadoFX in action, from setting up the project to building the UI, implementing game logic, and handling user interactions.

She showcased how to:
* Define views and components using TornadoFX’s builders.
* Apply styles using type-safe CSS.
* Manage application state and events.
* Integrate game logic written in Kotlin.

While focusing on TornadoFX, Liz also touched upon broader software development principles, such as the importance of automated testing. She candidly mentioned her preference for unit tests and the need for more in her demo project due to deadline constraints, reminding attendees about the test pyramid. This practical demonstration of building a game provided a tangible example of what’s possible with TornadoFX and how it can accelerate desktop development.

TornadoFX and the Kotlin Ecosystem

Liz Keogh’s presentation positioned TornadoFX as a valuable addition to the Kotlin ecosystem, particularly for developers looking to build desktop applications. By providing a more Kotlin-idiomatic layer over JavaFX, TornadoFX lowers the barrier to entry for desktop development and makes it a more attractive option for the Kotlin community.

She also mentioned another personal project, “K Golf” (Kotlin Game of Life), a JavaFX application she uses for teaching Kotlin, hinting at her passion for both Kotlin and creating engaging learning experiences. Her talk inspired many Kotlin developers to explore TornadoFX for their desktop application needs, showcasing it as a productive and enjoyable way to leverage their Kotlin skills in a new domain. The session underscored the theme of Kotlin’s versatility, extending its reach effectively into desktop development.

Links:

PostHeaderIcon [DevoxxPL2019] Semantic Event Sourcing: A Case Study of Transitioning from CRUD to Log-Based State Management

Lecturer

Neil Boddy is an engineer at Goldman Sachs, working in master data management. His expertise focuses on scalable data architectures, including shifts from relational CRUD to NoSQL and event-sourcing techniques for handling complex, high-volume financial data.

Abstract

This article delves into the transition from traditional CRUD operations in relational databases to log-based state management using event sourcing in a master data management context at Goldman Sachs. It analyzes the motivations, engineering challenges, and solutions for managing large-scale, fragmented data flows. Concepts like immutable events, semantic metadata, and sharded clusters are examined, with emphasis on resilience, recovery, and standardized access. Implications for fault tolerance and scalability in data-intensive applications are explored through practical observations.

Challenges in Traditional CRUD-Based Master Data Management

Master data management (MDM) involves processes, tools, and policies to create and maintain critical data shared across an organization, such as financial instruments (bonds, equities) or static references (currencies, countries). Neil describes Goldman Sachs’ environment with hundreds of heterogeneous data flows, schemas, and databases processing millions of structurally complex records daily—e.g., a bond with 300 attributes expanding to thousands at runtime due to relationships.

Traditional CRUD persists only the latest state, leading to issues: schema proliferation, domain-specific expertise needs, and poor scalability for large set operations (e.g., universe comparisons). Sequential processing fragility exacerbates problems; out-of-order batches corrupt states, requiring forensic analysis, migrations, and business disruptions. Human errors, buggy code, or upstream issues cascade, with recovery being error-prone and stressful.

Neil contrasts this with accounting’s compensatory transactions, preserving audit trails—paralleling event sourcing’s immutable events to avoid irreversible damage.

Principles of Event Sourcing and Log-Based Management

Event sourcing reconstructs state from sequenced events, inspired by domain-driven design. Unlike CRUD’s mutable updates, events are append-only, capturing changes immutably. Neil illustrates with a shopping cart: CRUD shows final quantities, but events reveal full history (additions, removals), enabling replays and audits.

In MDM, data arrives fragmented (e.g., car analogy: engine, chassis batches). Aggregates ensure logical consistency, atomic storage, and intuitive structures. Using MongoDB as a document store and log, events are stored in collections, supporting sharding for scalability.

Two-dimensional time—business (effective date) and system (processing timestamp)—enables point-in-time queries. For Wednesday’s data viewed Friday, include all events; rewind system time for as-at views.

Technical Implementation with MongoDB

Goldman Sachs employs a pipe-and-filters architecture: staging, transformation, resolution, persistence. MongoDB’s document model accommodates multi-structured data without custom schemas, embracing event sourcing semantics (full universes, deltas, corrections).

Dual logs: one for business data (minimal indexes, large sets), another for semantic metadata (rich indexes, small footprint). Metadata captures batch types, times, and segments, facilitating precise queries.

Sharded clusters partition logs, enabling concurrent queries. Process: query metadata log for pointers, then concurrently fetch from business log across nodes. Deduplicate by grouping identities, selecting latest observing 2D time, and dropping tombstones.

Projections support use cases: full universes, changes across times/segments, replays with deduplication, historic analyses.

Code sample for MongoDB aggregation (simplified projection):

db.businessLog.aggregate([
  { $match: { identity: "100", businessTime: { $lte: ISODate("2023-03-03") } } },
  { $sort: { systemTime: -1 } },
  { $group: { _id: "$identity", latest: { $first: "$$ROOT" } } },
  { $match: { "latest.tombstone": { $ne: true } } }
]);

This fetches latest non-tombstoned record for an identity up to a business date.

Resilience, Recovery, and Performance Gains

Log-based approaches enhance fault tolerance: stale data redefines points without corruption; rogue data exclusion is straightforward. Parallel recovery reduces times (e.g., 30 hours to 4), supporting out-of-order processing.

Low-friction persistence eases backups, indexing, and maintenance. Concurrency spreads read/write loads; rich metadata enables hygiene controls (e.g., preventing base data on processed days).

Standardization reduces variation, improving scalability over CRUD’s custom solutions.

Broader Implications and Observations

This shift yields richer data utility for computations (deltas, reconciliations) without data copying. Retired records are handled via log points, avoiding deletions. While new concepts and tech require learning, benefits include standardized access, tolerance to errors, and high availability via redundancy.

Neil notes applicability beyond MDM to batch-oriented, large-scale state management, though not universal. In conclusion, semantic event sourcing fortifies systems against fragility, promoting scalable, recoverable architectures in data-centric domains.

Links:

PostHeaderIcon [SpringIO2019] Cloud Native Spring Boot Admin by Johannes Edmeier

At Spring I/O 2019 in Barcelona, Johannes Edmeier, a seasoned developer from Germany, captivated attendees with his deep dive into managing Spring Boot applications in Kubernetes environments using Spring Boot Admin. As the maintainer of this open-source project, Johannes shared practical insights into integrating Spring Boot Admin with Kubernetes via the Spring Cloud Kubernetes project. His session illuminated how developers can gain operational visibility and control without altering application code, making it a must-know tool for cloud-native ecosystems. This post explores Johannes’ approach, highlighting its relevance for modern DevOps.

Understanding Spring Boot Admin

Spring Boot Admin, a four-and-a-half-year-old project boasting over 17,000 GitHub stars, is an Apache-licensed tool designed to monitor and manage Spring Boot applications. Johannes, employed by ConSol, a German consultancy, dedicates 20% of his work time—and significant personal hours—to its development. The tool provides a user-friendly interface to visualize metrics, logs, and runtime configurations, addressing the limitations of basic monitoring solutions like plain metrics or logs. For Kubernetes-deployed applications, it leverages Spring Boot Actuator endpoints to deliver comprehensive insights without requiring code changes or new container images.

The challenge in cloud-native environments lies in achieving visibility into distributed systems. Johannes emphasized that Kubernetes, a common denominator across cloud vendors, demands robust monitoring tools. Spring Boot Admin meets this need by integrating with Spring Cloud Kubernetes, enabling service discovery and dynamic updates as services scale or fail. This synergy ensures developers can manage applications seamlessly, even in complex, dynamic clusters.

Setting Up Spring Boot Admin on Kubernetes

Configuring Spring Boot Admin for Kubernetes is straightforward, as Johannes demonstrated. Developers start by including the Spring Boot Admin starter server dependency, which bundles the UI and REST endpoints, and the Spring Cloud Kubernetes starter for service discovery. These dependencies, managed via Spring Cloud BOM, simplify setup. Johannes highlighted the importance of enabling the admin server, discovery client, and scheduling annotations in the application class to ensure health checks and service updates function correctly. A common pitfall, recently addressed in the documentation, is forgetting to enable scheduling, which prevents dynamic service updates.

For Kubernetes deployment, Johannes pre-built a Docker image and configured a service account with role-based access control (RBAC) to read pod, service, and endpoint data. This minimal RBAC setup avoids unnecessary permissions, enhancing security. An ingress and service complete the deployment, allowing access to the Spring Boot Admin UI. Johannes showcased a wallboard view, ideal for team dashboards, and demonstrated real-time monitoring by simulating a service failure, which triggered a yellow “restricted” status and subsequent recovery as Kubernetes rescheduled the pod.

Enhancing Monitoring with Actuator Endpoints

Spring Boot Admin’s power lies in its integration with Spring Boot Actuator, which exposes endpoints like health, info, metrics, and more. By default, only health and info endpoints are exposed, but Johannes showed how to expose all endpoints using a Kubernetes environment variable (management.endpoints.web.exposure.include=*). This unlocks detailed views for metrics, environment properties, beans, and scheduled tasks. For instance, the health endpoint provides granular details when set to “always” show details, revealing custom health indicators like database connectivity.

Johannes also highlighted advanced features, such as rendering Swagger UI links via the info endpoint’s properties, simplifying access to API documentation. For security, he recommended isolating Actuator endpoints on a separate management port (e.g., 9080) to prevent public exposure via the main ingress. Spring Cloud Kubernetes facilitates this by allowing developers to specify the management port for discovery, ensuring Spring Boot Admin accesses Actuator endpoints securely while keeping them hidden from external traffic.

Customization and Security Considerations

Spring Boot Admin excels in customization, catering to specific monitoring needs. Johannes demonstrated how to add top-level links to external tools like Grafana or Kibana, or embed them as iframes, reducing the need to memorize URLs. For advanced use cases, developers can create custom views using Vue.js, as Johannes did to toggle application status (e.g., setting a service to “out of service”). This flexibility extends to notifications, supporting Slack, Microsoft Teams, and email via simple configurations, with a test SMTP server like MailHog for demos.

Security is a critical concern, as Spring Boot Admin proxies requests to Actuator endpoints. Johannes cautioned against exposing the admin server publicly, citing an unsecured instance found via Google. He outlined three security approaches: no authentication (not recommended), session-based authentication with cookies, or OAuth2 with token forwarding, where the target application validates access. A service account handles background health checks, ensuring minimal permissions. For Keycloak integration, Johannes referenced a blog post by his colleague Tomas, showcasing Spring Boot Admin’s compatibility with modern security frameworks.

Runtime Management and Future Enhancements

Spring Boot Admin empowers runtime management, a standout feature Johannes showcased. The loggers endpoint allows dynamic adjustment of logging levels, with a forthcoming feature to set levels across all instances simultaneously. Other endpoints, like Jolokia for JMX interaction, enable runtime reconfiguration but require caution due to their power. Heap and thread dump endpoints aid debugging but risk exposing sensitive data or overwhelming resources. Johannes also previewed upcoming features, like minimum instance checks, enhancing Spring Boot Admin’s robustness in production.

For Johannes, Spring Boot Admin is more than a monitoring tool—it’s a platform for operational excellence. By integrating seamlessly with Kubernetes and Spring Boot Actuator, it addresses the complexities of cloud-native applications, empowering developers to focus on delivering value. His session at Spring I/O 2019 underscores its indispensable role in modern software ecosystems.

Links:

PostHeaderIcon [DevoxxPL2019] Reactive for the Impatient: A Gentle Introduction to Reactive Programming and Systems

Lecturer

Mary Grygleski serves as a developer advocate at IBM, based in Chicago. She organizes the Chicago Java Users Group (CJUG) and leads IBM-sponsored meetups on topics like reactive systems and cloud technologies. Her background includes promoting community engagement and advancing Java-based reactive frameworks.

Abstract

This article provides an in-depth exploration of reactive programming and systems, emphasizing their emergence to address modern computing demands for responsiveness and scalability. It delineates core principles from the Reactive Manifesto, differentiates reactive paradigms, and surveys key Java libraries: RxJava, Spring Reactor, Akka, and Vert.x. Analytical insights into patterns, methodologies, and real-world applications underscore the significance of asynchronicity, elasticity, and fault tolerance in building impatient-user-friendly systems.

Emergence and Principles of Reactive Systems

The surge in reactive methodologies arises from hardware advancements, such as multi-core CPUs and cloud virtualization, coupled with escalating user expectations for instantaneous responses. Mary traces reactive roots to the 1980s actor model in Erlang for real-time telecommunications, now adapted to handle proliferating devices and concurrent requests. Human impatience drives this evolution, mirroring family dynamics where multiple demands require asynchronous handling.

The Reactive Manifesto, led by Lightbend (creators of Akka), outlines four pillars: responsiveness, elasticity, resiliency, and message-driven architecture. Responsiveness ensures timely replies, even in failures, forming the usability foundation. Elasticity scales resources dynamically under varying loads, maintaining throughput. Resiliency employs replication and isolation for fault containment, preventing systemic collapses. Message-driven mechanics enable the others, facilitating asynchronous, non-blocking communication akin to event-driven systems but with addressed destinations.

Mary clarifies distinctions: reactive programming propagates changes via event streams, functional reactive programming advances via execution threads, and reactive systems orchestrate isolated components cohesively. Event-driven emits unaddressed events for observers, while message-driven specifies recipients, enhancing coordination.

Patterns and Terminologies in Reactive Programming

Reactive programming revolves around responding to external stimuli through event propagation. Streams represent sequential data elements, fundamental to reactivity. Observables emit event streams, observed by subscribers, drawing from design patterns like observer, composite, and iterator.

Using marble diagrams, Mary illustrates streams: empty timelines await events, marbles denote data, vertical lines signal completion. Backpressure management prevents overwhelming consumers. Reactive extensions (Rx) standardize these, with RxJava implementing them in Java.

A noodle shop analogy piques interest: ordering mimics reactive flows, where requests (events) trigger preparations (responses) asynchronously, handling multiple patrons without blocking.

Survey of Java Reactive Libraries: RxJava and Spring Reactor

RxJava, Netflix’s 2013 port of Microsoft’s Reactive Extensions, supports Java 6+ with backpressure in version 2 (2016). It enables declarative, functional-style programming for asynchronous data streams.

Code sample for a simple observable:

import io.reactivex.Flowable;

public class HelloWorld {
    public static void main(String[] args) {
        Flowable.fromArray(args).subscribe(System.out::println);
    }
}

This pipelines arguments into a flowable, subscribing for output.

Spring Reactor, from Pivotal, leverages Java 8 streams for cleaner APIs, fully supporting reactive streams. It integrates with Kafka, Netty, and others.

Comparative example:

// Traditional Spring MVC (blocking)
@GetMapping("/products")
public List<Product> getProducts() {
    System.out.println("Traditional way started");
    List<Product> products = productService.getProducts();
    System.out.println("Traditional way completed");
    return products;
}

// Reactive WebFlux (non-blocking)
@GetMapping(value = "/product-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Product> getProductStream() {
    System.out.println("Reactive way using Flux started");
    Flux<Product> productFlux = productService.getProductStream();
    System.out.println("Reactive way using Flux completed");
    return productFlux;
}

The reactive version returns a Flux (ticket) immediately, processing asynchronously.

RxJava partially supports reactive streams; Reactor fully, with Reactor favoring Java 8+ for elegance.

Advanced Frameworks: Akka and Vert.x

Akka, from Lightbend, embodies the actor model for event-driven, location-transparent systems. Actors handle functions isolately, with supervisors managing failures for resiliency.

Java Akka hello world:

import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;

public class HelloWorld extends AbstractActor {
    @Override
    public void preStart() {
        final ActorRef greeter = getContext().actorOf(Props.create(Greeter.class), "greeter");
        greeter.tell(Greeter.Msg.GREET, getSelf());
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .matchEquals(Greeter.Msg.DONE, msg -> getContext().stop(getSelf()))
                .build();
    }
}

Scala variant condenses this, leveraging functional conciseness.

Vert.x, from Eclipse, is polyglot, supporting mixed languages. Verticles (actor-like) execute on events, with an event bus for communication.

Vert.x HTTP server:

import io.vertx.core.Vertx;

public class HelloWorldServer {
    public static void main(String[] args) {
        Vertx.vertx().createHttpServer()
                .requestHandler(req -> req.response().end("Hello World"))
                .listen(8080);
    }
}

Vert.x’s lightweight, non-container-bound nature suits diverse integrations.

Implications for Modern Software Development

Reactive approaches mitigate blocking I/O pitfalls, though database engines lag in full reactivity (e.g., R2DBC offers non-blocking connectivity, but underlying engines remain blocking). Mary advocates community participation, like her reactive meetup group, to foster learning.

In conclusion, reactive paradigms empower scalable, responsive systems, aligning software with hardware and user demands. Frameworks like RxJava, Reactor, Akka, and Vert.x provide tools for implementation, promising flexible, fault-tolerant architectures.

Links:

PostHeaderIcon Kotlin Native Concurrency Explained by Kevin Galligan

Navigating Kotlin/Native’s Concurrency Model

At KotlinConf 2019 in Copenhagen, Kevin Galligan, a partner at Touchlab with over 20 years of software development experience, delivered a 39-minute talk on Kotlin/Native’s concurrency model. Kevin Galligan explored the restrictive yet logical rules governing state and concurrency in Kotlin/Native, addressing their controversy among JVM and mobile developers. He explained the model’s mechanics, its rationale, and best practices for multiplatform development. This post covers four key themes: the core rules of Kotlin/Native concurrency, the role of workers, the impact of freezing state, and the introduction of multi-threaded coroutines.

Core Rules of Kotlin/Native Concurrency

Kevin Galligan began by outlining Kotlin/Native’s two fundamental concurrency rules: mutable state is confined to a single thread, and immutable state can be shared across multiple threads. These rules, known as thread confinement, mirror mobile development practices where UI updates are restricted to the main thread. In Kotlin/Native, the runtime enforces these constraints, preventing mutable state changes from background threads to avoid race conditions. Kevin emphasized that while these rules feel restrictive compared to the JVM’s shared-memory model, they align with modern platforms like Go and Rust, which also limit unrestricted shared state.

The rationale behind this model, as Kevin explained, is to reduce concurrency errors by design. Unlike the JVM, which trusts developers to manage synchronization, Kotlin/Native’s runtime verifies state access at runtime, crashing if rules are violated. This strictness, though initially frustrating, encourages intentional state management. Kevin noted that after a year of working with Kotlin/Native, he found the model simple and effective, provided developers embrace its constraints rather than fight them.

Workers as Concurrency Primitives

A central concept in Kevin’s talk was the Worker, a Kotlin/Native concurrency queue similar to Java’s ExecutorService or Android’s Handler and Looper. Workers manage a job queue processed by a private thread, ensuring thread confinement. Kevin illustrated how a Worker executes tasks via the execute function, which takes a producer function to verify state transfer between threads. The execute function supports safe and unsafe transfer modes, with Kevin strongly advising against the unsafe mode due to its bypassing of state checks.

Using a code example, Kevin demonstrated passing a data class to a Worker. The runtime freezes the data—making it immutable—to comply with concurrency rules, preventing illegal state transfers. He highlighted that while Worker is a core primitive, developers rarely use it directly, as higher-level abstractions like coroutines are preferred. However, understanding Worker is crucial for grasping Kotlin/Native’s concurrency mechanics, especially when debugging state-related errors like IllegalStateTransfer.

Freezing State and Its Implications

Kevin Galligan delved into the concept of freezing, a runtime mechanism that designates objects as immutable for safe sharing across threads. Freezing is a one-way operation, recursively applying to an object and its references, with no unfreeze option. This ensures thread safety but introduces challenges, as frozen objects cannot be mutated, leading to InvalidMutabilityException errors if attempted.

In a practical example, Kevin showed how capturing mutable state in a background task can inadvertently freeze an entire object graph, causing runtime failures. He introduced tools like ensureNeverFrozen to debug unintended freezing and stressed intentional mutability—keeping mutable state local to one thread and transforming data into frozen copies for sharing. Kevin also discussed Atomic types, which allow limited mutation of frozen state, but cautioned against overusing them due to performance and memory issues. His experience at Touchlab revealed early missteps with global state and Atomics, leading to a shift toward confined state models.

Multi-Threaded Coroutines and Future Directions

A significant update in Kevin’s talk was the introduction of multi-threaded coroutines, enabled by a draft pull request in 2019. Previously, Kotlin/Native coroutines were single-threaded, limiting concurrency and stunting library development. The new model allows coroutines to switch threads using dispatchers, with data passed between threads frozen to maintain strict mode. Kevin demonstrated replacing a custom background function with a coroutine-based approach, simplifying concurrency while adhering to state rules.

This development clarified the longevity of strict mode, countering speculation about a relaxed mode that would mimic JVM-style shared memory. Kevin noted that multi-threaded coroutines unblocked library development, citing projects like AtomicFu and SQLDelight. He also highlighted Touchlab’s Droidcon app, which adopted multi-threaded coroutines for production, showcasing their practical viability. Looking forward, Kevin anticipated increased community adoption and library growth in 2020, urging developers to explore the model despite its learning curve.

Conclusion

Kevin Galligan’s KotlinConf 2019 talk demystifies Kotlin/Native’s concurrency model, offering a clear path for developers navigating its strict rules. By embracing thread confinement, leveraging workers, managing frozen state, and adopting multi-threaded coroutines, developers can build robust multiplatform applications. This talk is a must for Kotlin/Native enthusiasts seeking to master concurrency in modern mobile development.

Hashtags: #KevinGalligan #KotlinNative #Concurrency #Touchlab #JetBrains #Multiplatform

PostHeaderIcon [DevoxxPL2019] Micro Frontends: Extending Service-Oriented Architecture to Frontend Development

Lecturer

Jakub Sowiński is a software architect at StepStone Services, specializing in frontend web development. He joined the company four years prior to his 2019 presentation as a software engineer, focusing on the maintenance and development of their core online job board platform. His work emphasizes architectural transformations from monolithic systems to service-oriented designs, particularly in frontend contexts.

Abstract

This article explores the adoption of micro frontends as an extension of service-oriented architecture to frontend development, drawing from practical experiences at StepStone Services. It examines the rationale, implementation challenges, and benefits of decomposing frontend applications into independent, deployable units. Key concepts such as independence in deployment, team ownership, and progressive refactoring are analyzed, alongside technical strategies for composition, communication, and standardization. The implications for organizational structure, development agility, and system resilience are discussed, highlighting how this approach addresses complexities in large-scale, distributed systems.

Context and Rationale for Micro Frontends

In the evolving landscape of software architecture, the shift from monolithic applications to service-oriented designs has become a cornerstone for managing complexity in backend systems. Jakub extends this paradigm to the frontend, introducing micro frontends as a means to handle the user interface in distributed environments. At StepStone Services, the core application—an online job board—initially presented as a sprawling monolith with millions of lines of code, lacking modularity and separation. This led to challenges in adding features without introducing bugs, slowed release cycles (once weekly), and difficulties in maintaining code quality.

The motivation stems from organizational and technical imperatives. Micro frontends allow for vertically decomposed applications, where each segment encapsulates a specific business logic subdomain, owned by autonomous teams. This fosters expertise within teams, enhances developer satisfaction, and aligns with agile principles by enabling rapid iterations and experiments. Jakub references industry adoption by companies like Facebook and Microsoft, underscoring the traction gained by this approach in recent years, particularly since 2015 when the term gained prominence.

Critically, this method addresses Dan Abramov’s critique, where he questioned the necessity of micro frontends, suggesting component models suffice. Jakub counters that while component models handle modularity, micro frontends tackle broader organizational structures, promoting small, focused teams that deliver end-to-end value. The architecture facilitates progressive refactoring, minimizing risks by isolating dependencies, a vital aspect for legacy systems like StepStone’s.

Implementation Strategies and Technical Solutions

Implementing micro frontends requires a composition layer, often termed a container application or templating engine, to assemble independent micro applications into a cohesive user experience. At StepStone, a modified version of Zalando’s Taylor library handles this, using configuration files to map routes to templates and fragments. Templates are collections of fragments, each with a unique ID linking to specific micro frontends. This server-side composition ensures the end-user perceives a unified website, while under the hood, each micro frontend maintains its own repository, pipeline, and version.

Inter-micro frontend communication poses another challenge. Jakub describes using PubSubJS for publisher-subscriber patterns, where components subscribe to messages (e.g., triggering a login modal). Alternatives like custom events via browser APIs or shared global states (e.g., Redux) are viable, though StepStone favors PubSubJS for simplicity. For development processes, standardization mitigates fragmentation risks. A project creation tool generates skeletons with standardized tech stacks (React, TypeScript, Webpack), build plans (Babel compilation, Jest testing), and deployment options (Node.js for server-side rendering or static assets).

Styling consistency is achieved via CSS-in-JS with styled-components, allowing theme variants for different websites, managed by UX teams. A component library in a monorepo, using tools like Storybook and Lerna, ensures reusability. Testing standardization includes unit tests in build plans and an automated test framework with Selenium, split by subdomains for efficient releases.

Code sample illustrating fragment composition in the templating engine:

// Example route-to-template mapping
const routes = {
  '/home': 'homeTemplate',
  '/search': 'searchTemplate'
};

// Exemplary template with fragments
const homeTemplate = `
  <header id="headerFragment"></header>
  <main id="contentFragment"></main>
  <footer id="footerFragment"></footer>
`;

// Fragment mapping
const fragments = {
  'headerFragment': { url: '/microfrontend/header', config: { /* options */ } },
  // Additional fragments...
};

Challenges and Mitigation Approaches

Adopting micro frontends introduces complexities, such as potential fragmentation in processes and technologies. Jakub acknowledges risks like decreased consistency but argues autonomy boosts responsibility and effectiveness. Balancing this requires automation and standards, as seen in StepStone’s tools for project setup and shared libraries (e.g., frontend vendor package for common dependencies like React, reducing bundle sizes).

Performance benefits arise from externalizing shared libraries, cached early in user sessions. For testing, baseline standards ensure coverage, with teams encouraged to experiment (e.g., Cypress). Ownership models, inspired by open-source practices, appoint custodians for shared tools, preventing neglect. Community meetings facilitate alignment, empowering developers to solve common issues collectively.

A key pain point is out-of-order processing resilience. Unlike CRUD systems, where sequential errors corrupt states, log-based approaches (though not directly used here) inspire fault tolerance. Micro frontends’ independence minimizes cascading failures, enabling quicker recoveries.

Implications and Future Directions

The implications extend beyond technical gains to organizational agility. StepStone reduced release times from a week to 30 minutes, enhancing speed without meetings or extensive testing. This supports continuous delivery in large-scale applications, with benefits like progressive refactoring allowing graceful replacements.

However, Jakub cautions that micro frontends suit specific contexts—large, complex applications—not all projects. Starting with monoliths is advisable for simplicity, as premature decomposition increases overhead. Future enhancements could integrate web components for greater tech autonomy, though StepStone prioritizes standardization for collaboration and performance.

In conclusion, micro frontends represent a strategic extension of service-oriented principles, fostering scalable, resilient frontends. StepStone’s journey illustrates practical viability, balancing autonomy with standards to drive innovation and efficiency.

Links: