Edit

Share via


Reliable Web App pattern for Java

This article provides guidance to implement the Reliable Web App pattern. This pattern describes how to replatform web apps for cloud migration. It provides prescriptive architecture, code, and configuration guidance that aligns with the principles of the Azure Well-Architected Framework.

Why use the Reliable Web App pattern for Java?

The Reliable Web App pattern is a set of principles and implementation techniques that define how to replatform web apps when you migrate them to the cloud. It emphasizes minimal code updates to ensure success in the cloud. This guidance uses a reference implementation as a consistent example throughout. It follows the replatforming journey of the fictional company Contoso Fiber to provide business context for your own migration. Before Contoso Fiber implements the Reliable Web App pattern for Java, it operates a monolithic, on-premises Customer Account Management System (CAMS) built with the Spring Boot framework.

Tip

GitHub logo. The reference implementation (sample) of the Reliable Web App pattern represents the final state of a completed implementation. This production-grade web app includes all the code, architecture, and configuration updates described in this article. Deploy and use the reference implementation to help guide your own implementation of the Reliable Web App pattern.

How to implement the Reliable Web App pattern

Find the specific guidance that you need in the following sections of this article:

  • Business context: Align this guidance with your business context and learn to define immediate and long-term goals that drive replatforming decisions.

  • Architecture guidance: Select the right cloud services and design an architecture that meets your business requirements.

  • Code guidance: Implement the Retry, Circuit Breaker, and Cache-Aside design patterns to improve the reliability and performance efficiency of your web app in the cloud.

  • Configuration guidance: Configure authentication and authorization, managed identities, rightsized environments, infrastructure as code (IaC), and monitoring.

Business context

The first step in replatforming a web app is to define your business objectives. Set immediate goals such as service-level objectives (SLOs) and cost optimization targets, along with future goals for your web application. These objectives influence your choice of cloud services and the architecture of your application in the cloud. Define a target SLO for your web app, such as 99.9% uptime. Calculate the composite service-level agreement (SLA) for all the services that affect the availability of your web app.

Contoso Fiber wants to expand its on-premises CAMS web app to reach other regions. To meet the increased demand on the web app, the company establishes the following goals:

  • Apply low-cost, high-value code changes.
  • Reach an SLO of 99.9%.
  • Adopt DevOps practices.
  • Create cost-optimized environments.
  • Improve reliability and security.

Contoso Fiber determines that its on-premises infrastructure isn't a cost-effective solution for scaling the application. The company decides that migrating the CAMS web application to Azure is the most cost-effective way to achieve its immediate and future objectives.

Architecture guidance

The Reliable Web App pattern has a few essential architectural elements. You need Domain Name System (DNS) to manage endpoint resolution, a web application firewall to block malicious HTTP traffic, and a load balancer to route and help protect inbound user requests. The application platform hosts your web app code and calls the back-end services through private endpoints in a virtual network. An application performance monitoring tool captures metrics and logs to help you understand your web app.

Diagram that shows the essential architectural elements of the Reliable Web App pattern.

Design the architecture

Design your infrastructure to support your recovery metrics, like your recovery time objective (RTO) and recovery point objective (RPO). The RTO affects availability and must support your SLO. Determine an RPO and configure data redundancy to meet the RPO.

Select the right Azure services

When you move a web app to the cloud, choose Azure services that meet your business requirements and align with the features of the on-premises web app. This alignment helps minimize the replatforming effort. For example, use services that allow you to keep the same database engine and support existing middleware and frameworks.

Before migration, Contoso Fiber's CAMS web app is an on-premises, monolithic Java application. It's a Spring Boot app with a PostgreSQL database. The web app is a line-of-business (LOB) support app for employees. They use it to manage customer support cases. The app experiences common challenges with scalability and feature deployment. This starting point, along with business goals and SLOs, influences their service choices.

The following list provides guidance to select the right Azure services for your web app and describes why Contoso Fiber selects specific services:

  • Application platform: Use Azure App Service as your application platform. Contoso Fiber uses App Service for the following reasons:

    • Natural progression: Contoso Fiber deploys a Spring Boot JAR file on its on-premises server and wants to minimize the amount of rearchitecting for that deployment model. App Service provides robust support to run Spring Boot apps, which makes it a suitable option. Azure Container Apps is also a suitable option for this application. For more information, see Container Apps overview and Java on Container Apps overview.

    • High SLA: App Service provides a high SLA that meets production requirements.

    • Reduced management overhead: App Service is a managed hosting solution.

    • Containerization capability: App Service integrates with private container image registries like Azure Container Registry. Contoso Fiber can use these registries to containerize the web app in the future.

    • Autoscaling: The web app can rapidly scale up, scale down, scale in, and scale out based on user traffic.

  • Identity management: Use Microsoft Entra ID as your identity and access management solution. Contoso Fiber uses Microsoft Entra ID for the following reasons:

    • Authentication and authorization: The application needs to authenticate and authorize call center employees.

    • Scalability: Microsoft Entra ID scales to support larger scenarios.

    • User-identity control: Call center employees can use their existing enterprise identities.

    • Authorization protocol support: Microsoft Entra ID supports OAuth 2.0 for managed identities.

  • Database: Use a service that lets you keep the same database engine. Use the data store decision tree to guide your selection. Contoso Fiber uses the Azure Database for PostgreSQL flexible server deployment model for the following reasons:

    • Reliability: The flexible server deployment model supports zone-redundant high availability across multiple availability zones. This configuration maintains a warm standby server in a different availability zone within the same Azure region. The configuration replicates data synchronously to the standby server.

    • Cross-region replication: Azure Database for PostgreSQL provides a read replica feature to asynchronously replicate data to a read-only replica database in another region.

    • Performance: Azure Database for PostgreSQL provides predictable performance and intelligent tuning that improves database performance by using real usage data.

    • Reduced management overhead: This managed Azure service reduces management obligations.

    • Migration support: It supports database migration from on-premises single-server PostgreSQL databases. Contoso Fiber can use the migration tool to simplify the migration process.

    • Consistency with on-premises configurations: It supports different community versions of PostgreSQL, including the version that Contoso Fiber currently uses.

    • Resiliency: The flexible server deployment automatically creates server backups and stores them in zone-redundant storage (ZRS) within the same region. Contoso Fiber can restore the database to any point in time within the backup retention period. The backup and restoration capability creates a better RPO compared to on-premises environments.

  • Application performance monitoring: Use Application Insights to analyze telemetry on your application. Contoso Fiber uses Application Insights for the following reasons:

    • Integration with Azure Monitor: It provides the best integration with Azure Monitor.

    • Anomaly detection: It automatically detects performance anomalies.

    • Troubleshooting: It helps diagnose problems in the running app.

    • Monitoring: It collects usage data for the app and tracks custom events.

    • Visibility gap: The on-premises solution doesn't have an application performance monitoring solution. Application Insights provides easy integration with the application platform and code.

  • Cache: Choose whether to add a cache to your web app architecture. Azure Managed Redis is the primary Azure cache solution. It's a managed in-memory data store based on the Redis software. Contoso Fiber adds Azure Managed Redis for the following reasons:

    • Speed and volume: It provides high-data throughput and low-latency reads for frequently accessed, slow-changing data.

    • Diverse supportability: It's a unified cache location that all instances of the web app can use.

    • External data store: The on-premises application servers use VM-local caching. This setup doesn't offload frequently accessed data and can't invalidate stale data.

    • Nonsticky sessions: The cache lets the web app externalize session state and use nonsticky sessions. Most Java web apps that run on-premises rely on in-memory client-side caching. This approach doesn't scale well and increases the memory footprint on the host. Azure Managed Redis provides a managed, scalable cache service to improve the scalability and performance of applications. Contoso Fiber used Spring Cache as their cache abstraction framework and needed only minimal configuration changes to switch from an Ehcache provider to the Redis provider.

  • Load balancer: Web applications that use platform as a service (PaaS) solutions should use Azure Front Door, Azure Application Gateway, or both, depending on the web app architecture and requirements. Use the load balancer decision tree to select the right load balancer. Contoso Fiber needs a layer-7 load balancer that can route traffic across multiple regions and a multiregion web app to meet the SLO of 99.9%. The company uses Azure Front Door for the following reasons:

    • Global load balancing: This layer-7 load balancer can route traffic across multiple regions.

    • Web application firewall: It integrates natively with Azure Web Application Firewall.

    • Routing flexibility: It allows the application team to configure ingress needs to support future changes in the application.

    • Traffic acceleration: It uses anycast routing to reach the nearest Azure point of presence and find the fastest route to the web app.

    • Custom domains: It supports custom domain names with flexible domain validation.

    • Health probes: The application needs intelligent health probe monitoring. Azure Front Door uses responses from the probe to determine the best origin for routing client requests.

    • Monitoring support: Azure Front Door supports built-in reports with an all-in-one dashboard for both Azure Front Door and security patterns. It provides alerts that integrate with Azure Monitor. Azure Front Door lets the application log each request and failed health probes.

    • Distributed denial-of-service (DDoS) protection: It has built-in DDoS protection at layer 3 and layer 4.

    • Content delivery network: It positions Contoso Fiber to use a content delivery network. The content delivery network provides site acceleration.

  • Web application firewall: Use Azure Web Application Firewall to provide centralized protection from common web exploits and vulnerabilities. Contoso Fiber uses Azure Web Application Firewall for the following reasons:

    • Global protection: It provides improved global web app protection while maintaining performance.

    • Botnet protection: The team can monitor and configure settings to address security concerns related to botnets.

    • Parity with on-premises: The on-premises solution runs behind a web application firewall that IT manages.

    • Ease of use: Azure Web Application Firewall integrates with Azure Front Door.

  • Secrets manager: Use Azure Key Vault if you have secrets to manage in Azure. Contoso Fiber uses Key Vault for the following reasons:

    • Encryption: It supports encryption at rest and in transit.

    • Managed identity support: The application services can use managed identities to access the secret store.

    • Monitoring and logging: Key Vault facilitates audit access and generates alerts when stored secrets change.

    • Integration: Key Vault provides native integration with the Azure configuration store (Azure App Configuration) and web hosting platform (App Service).

  • Endpoint security: Use Azure Private Link to access PaaS solutions over a private endpoint in your virtual network. Traffic between your virtual network and the service travels across the Microsoft backbone network. Contoso Fiber uses Private Link for the following reasons:

    • Enhanced-security communication: It lets the application privately access services on the Azure platform and reduces the network footprint of data stores to help protect against data leakage.

    • Minimal effort: The private endpoints support the web app platform and database platform that the web app uses. Both platforms mirror existing on-premises configurations, so minimal change is required.

  • Network security: Use Azure Firewall to control inbound and outbound traffic at the network level. Use Azure Bastion to connect to VMs with enhanced security, without exposing Remote Desktop Protocol/Secure Shell (RDP/SSH) ports. Contoso Fiber adopts a hub-and-spoke network topology and puts shared network security services in the hub. Azure Firewall inspects outbound traffic from the spokes to enhance network security. The company uses Azure Bastion for enhanced-security deployments from a jump host in the DevOps subnet.

Code guidance

To successfully move a web app to the cloud, you need to update your web app code by using the Retry, Circuit Breaker, and Cache-Aside patterns.

Diagram that shows the roles of design patterns in the Reliable Web App pattern.

The following design patterns provide workload benefits that map to one or more of the Well-Architected Framework pillars:

  1. The Retry pattern handles transient failures by retrying operations that might fail intermittently. Implement this pattern on all outbound calls to other Azure services.

  2. The Circuit Breaker pattern prevents an application from retrying operations that aren't transient. Implement this pattern in all outbound calls to other Azure services.

  3. The Cache-Aside pattern loads data on demand into a cache from a data store. Implement this pattern on requests to the database.

Design pattern Reliability (RE) Security (SE) Cost Optimization (CO) Operational Excellence (OE) Performance Efficiency (PE) Supporting WAF principles
Retry pattern RE:07
Circuit Breaker pattern RE:03
RE:07
PE:07
PE:11
Cache-Aside pattern RE:05
PE:08
PE:12

Implement the Retry pattern

Add the Retry pattern to your application code to address temporary service disruptions. These disruptions are called transient faults. Transient faults usually resolve themselves within seconds. You can use the Retry pattern to resend failed requests. It also allows you to configure the delay between retries and the number of attempts to make before conceding failure.

Use Resilience4j, a lightweight fault-tolerance library, to implement the Retry pattern in Java. To add the Retry pattern, the reference implementation decorates the service plan controller's listServicePlans method with Retry annotations. The code retries the call to a list of service plans from the database if the initial call fails. The retry policy for the reference implementation includes maximum attempts, wait duration, and exceptions to retry. Configure the retry policy in the application.properties file.

    @GetMapping("/list")
    @PreAuthorize("hasAnyAuthority('APPROLE_AccountManager')")
    @CircuitBreaker(name = SERVICE_PLAN)
    @Retry(name = SERVICE_PLAN)
    public String listServicePlans(Model model) {
        List<serviceplandto> servicePlans = planService.getServicePlans();
        model.addAttribute("servicePlans", servicePlans);
        return "pages/plans/list";
    }

Implement the Circuit Breaker pattern

Use the Circuit Breaker pattern to handle service disruptions that aren't transient faults. The Circuit Breaker pattern prevents an application from continuously attempting to access a nonresponsive service. It releases the application and helps prevent wasting central processing unit (CPU) cycles so that the application retains its performance integrity for users.

Use Spring Cloud Circuit Breaker and Resilience4j to implement the Circuit Breaker pattern. The reference implementation applies the Circuit Breaker pattern by decorating methods with the Circuit Breaker attribute.

Implement the Cache-Aside pattern

Add the Cache-Aside pattern to your web app to improve in-memory data management. The pattern assigns the application the responsibility of handling data requests and ensuring consistency between the cache and persistent storage, such as a database. It shortens response times, enhances throughput, and reduces the need for more scaling. It also reduces the load on the primary datastore, which improves reliability and cost optimization. To implement the Cache-Aside pattern, follow these recommendations:

  • Configure the application to use a cache. To enable caching, add the spring-boot-starter-cache package as a dependency in your pom.xml file. This package provides default configurations for Redis cache.

  • Cache high-need data. Apply the Cache-Aside pattern on high-need data to enhance its effectiveness. Use Azure Monitor to track the CPU, memory, and storage of the database. These metrics help you determine whether you can use a smaller database SKU after you apply the Cache-Aside pattern. To cache specific data in your code, add the @Cacheable annotation. This annotation specifies to Spring which methods should have their results cached.

  • Keep cache data fresh. Schedule regular cache updates to sync with the latest database changes. Use data volatility and user needs to determine the optimal refresh rate. This practice ensures that the application uses the Cache-Aside pattern to provide rapid access and current information. The default cache settings might not suit your web application. You can customize these settings in the application.properties file or the environment variables. For instance, you can modify the spring.cache.redis.time-to-live value (expressed in milliseconds) to control how long data should remain in the cache before it's removed.

  • Ensure data consistency. Implement mechanisms to update the cache immediately after database write operations. Use event-driven updates or dedicated data management classes to ensure cache coherence. Consistently synchronizing the cache with database modifications is central to the Cache-Aside pattern.

Configuration guidance

The following sections provide guidance about how to implement the configuration updates. Each section aligns with one or more pillars of the Well-Architected Framework.

Configuration Reliability (RE) Security (SE) Cost Optimization (CO) Operational Excellence (OE) Performance Efficiency (PE) Supporting WAF principles
Configure user authentication and authorization SE:05
OE:10
Implement managed identities SE:05
OE:10
Rightsize environments CO:05
CO:06
Implement autoscaling RE:06
CO:12
PE:05
Automate resource deployment OE:05
Implement monitoring OE:07
PE:04

Configure user authentication and authorization

When you migrate web applications to Azure, configure user authentication and authorization mechanisms. Follow these recommendations:

  • Use an identity platform. Use the Microsoft identity platform for developers to set up web app authentication. This platform supports applications that use a single Microsoft Entra directory, multiple Microsoft Entra directories from different organizations, and Microsoft identities or social accounts.

    The [Spring Boot Starter for Microsoft Entra ID](/azure/developer/java/spring-framework/spring-boot-starter-for-entra-developer-guide uses Spring Security and Spring Boot to ensure easy configuration and integration. It provides various authentication flows, automatic token management, customizable authorization policies, and integration capabilities with Spring Cloud components. This tool enables straightforward Microsoft Entra ID and OAuth 2.0 integration into Spring Boot applications without manual library or settings configuration.

    The reference implementation uses the Microsoft identity platform (Microsoft Entra ID) as the identity provider for the web app. It uses the OAuth 2.0 authorization code grant to sign in a user who has a Microsoft Entra account. The following XML snippet defines the two required dependencies of the OAuth 2.0 authorization code grant flow. The dependency com.azure.spring: spring-cloud-azure-starter-active-directory enables Microsoft Entra authentication and authorization in a Spring Boot application. The dependency org.springframework.boot: spring-boot-starter-oauth2-client enables OAuth 2.0 authentication and authorization in a Spring Boot application.

    <dependency>
        <groupid>com.azure.spring</groupid>
        <artifactid>spring-cloud-azure-starter-active-directory</artifactid>
    </dependency>
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-oauth2-client</artifactid>
    </dependency>
    
  • Create an application registration. Microsoft Entra ID requires an application registration in the primary tenant. The application registration helps ensure that users who get access to the web app have identities in the primary tenant. The reference implementation uses Terraform to create a Microsoft Entra ID app registration together with an app-specific Account Manager role:

    resource "azuread_application" "app_registration" {
      display_name     = "${azurecaf_name.app_service.result}-app"
      owners           = [data.azuread_client_config.current.object_id]
      sign_in_audience = "AzureADMyOrg"  # single tenant
    
      app_role {
        allowed_member_types = ["User"]
        description          = "Account Managers"
        display_name         = "Account Manager"
        enabled              = true
        id                   = random_uuid.account_manager_role_id.result
        value                = "AccountManager"
      }
    }
    
  • Enforce authorization in the application. Use role-based access control (RBAC) to assign least privileges to application roles. Define specific roles for different user actions to avoid overlap and ensure clarity. Map users to the appropriate roles and ensure that they have access only to necessary resources and actions. Configure Spring Security to use Spring Boot Starter for Microsoft Entra ID. This library enables integration with Microsoft Entra ID and helps ensure that users are authenticated securely. Configure and enable the Microsoft Authentication Library (MSAL) to get access to more security features. These features include token caching and automatic token refreshing.

    The reference implementation creates app roles that reflect the types of user roles in Contoso Fiber's account management system. Roles translate into permissions during authorization. Examples of app-specific roles in CAMS include Account Manager, Level One (L1) Support Representative, and Field Service Representative. The Account Manager role has permissions to add new app users and customers. A Field Service Representative can create support tickets. The PreAuthorize attribute restricts access to specific roles.

        @GetMapping("/new")
        @PreAuthorize("hasAnyAuthority('APPROLE_AccountManager')")
        public String newAccount(Model model) {
            if (model.getAttribute("account") == null) {
                List<ServicePlan> servicePlans = accountService.findAllServicePlans();
                ServicePlan defaultServicePlan = servicePlans.stream().filter(sp -> sp.getIsDefault() == true).findFirst().orElse(null);
                NewAccountRequest accountFormData = new NewAccountRequest();
                accountFormData.setSelectedServicePlanId(defaultServicePlan.getId());
                model.addAttribute("account", accountFormData);
                model.addAttribute("servicePlans", servicePlans);
            }
            model.addAttribute("servicePlans", accountService.findAllServicePlans());
            return "pages/account/new";
        }
        ...
    

    To integrate with Microsoft Entra ID, the reference implementation uses the OAuth 2.0 authorization code grant flow. This flow enables a user to sign in by using a Microsoft account. The following code snippet shows how to configure the SecurityFilterChain to use Microsoft Entra ID for authentication and authorization.

    @Configuration(proxyBeanMethods = false)
    @EnableWebSecurity
    @EnableMethodSecurity
    public class AadOAuth2LoginSecurityConfig {
        @Bean
        SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
                .and()
                    .authorizeHttpRequests()
                .requestMatchers(EndpointRequest.to("health")).permitAll()
                .anyRequest().authenticated()
                .and()
                    .logout(logout -> logout
                                .deleteCookies("JSESSIONID", "XSRF-TOKEN")
                                .clearAuthentication(true)
                                .invalidateHttpSession(true));
            return http.build();
        }
    }
    ...
    
  • Prefer temporary access to storage. Use temporary permissions to safeguard against unauthorized access and breaches. For example, you can use shared access signatures (SAS) to limit access to a period of time. Use user delegation SAS to maximize security when you grant temporary access. It's the only SAS that uses Microsoft Entra ID credentials and doesn't require a permanent storage account key.

  • Enforce authorization in Azure. Use Azure RBAC) to assign least privileges to user identities. Azure RBAC defines the Azure resources that identities can access, what they can do with those resources, and the areas that they have access to.

  • Avoid permanent elevated permissions. Use Microsoft Entra Privileged Identity Management (PIM) to grant just-in-time (JIT) access for privileged operations. For example, developers often need administrator-level access to create and delete databases, modify table schemas, and change user permissions. When you use JIT access, user identities receive temporary permissions to perform privileged tasks.

Implement managed identities

Use managed identities for all Azure services that support them. A managed identity allows Azure resources, specifically workload identities, to authenticate to and interact with other Azure services without requiring you to manage credentials. To simplify the migration, you can continue to use on-premises authentication solutions for hybrid and legacy systems, but you should transition them to managed identities as soon as possible. To implement managed identities, follow these recommendations:

  • Select the right type of managed identity. Prefer user-assigned managed identities when you have two or more Azure resources that need the same set of permissions. This approach is more efficient than creating system-assigned managed identities for each of those resources and assigning the same permissions to all of them. Otherwise, use system-assigned managed identities.

  • Configure least privileges. Use Azure RBAC to grant only permissions that are critical for operations, like create, read, update, and delete (CRUD) actions in databases or accessing secrets. Workload identity permissions are persistent, so you can't provide JIT or short-term permissions to workload identities. If Azure RBAC doesn't cover a specific scenario, supplement Azure RBAC with Azure service-level access policies.

  • Provide security for remaining secrets. Store any remaining secrets in Key Vault. Load secrets from Key Vault at application startup instead of during each HTTP request. High-frequency access within HTTP requests can exceed Key Vault transaction limits. Store application configurations in App Configuration.

Rightsize environments

Use performance tiers (SKUs) of Azure services that meet the needs of each environment without exceeding them. To rightsize your environments, do the following actions:

  • Estimate costs. Use the Azure pricing calculator to estimate the cost of each environment.

  • Optimize production environments. Production environments need SKUs that meet the SLA, features, and scale needed for production. Continuously monitor resource usage and adjust SKUs to align with actual performance needs.

  • Optimize preproduction environments. Preproduction environments should use lower-cost resources and take advantage of discounts like Azure plan for dev/test pricing. In these environments, disable services that aren't needed. Also ensure that preproduction environments are sufficiently similar to production environments to avoid introducing risks. Maintain this balance to ensure that testing remains effective without incurring unnecessary costs.

  • Use IaC to define SKUs. Implement IaC to dynamically select and deploy the correct SKUs based on the environment. This approach enhances consistency and simplifies management.

For example, the reference implementation has an optional parameter that specifies the SKU to deploy. An environment parameter specifies that the Terraform template should deploy development SKUs:

azd env set APP_ENVIRONMENT prod

Implement autoscaling

Autoscaling helps ensure that a web app remains resilient, responsive, and capable of handling dynamic workloads efficiently. To implement autoscaling, follow these recommendations:

  • Automate scale-out. Use Azure autoscale to automate horizontal scaling in production environments. Configure autoscaling rules to scale out based on key performance metrics so that your application can handle varying loads.

  • Refine scaling triggers. Use CPU utilization as your initial scaling trigger if you're unfamiliar with your application’s scaling requirements. Refine your scaling triggers to include other metrics like RAM, network throughput, and disk input/output (I/O). The goal is to match your web application's behavior for better performance.

  • Provide a scale-out buffer. Set your scaling thresholds to initiate scaling before maximum capacity is reached. For example, configure scaling to occur at 85% CPU utilization rather than waiting until it reaches 100%. This proactive approach helps maintain performance and avoid potential bottlenecks.

Automate resource deployment

Use automation to deploy and update Azure resources and code across all environments. Follow these recommendations:

  • Use IaC. Deploy IaC by using continuous integration and continuous delivery (CI/CD) pipelines. Azure provides prebuilt Bicep templates, Azure Resource Manager templates (ARM templates) JSON, and Terraform templates for every Azure resource.

  • Use a CI/CD pipeline. Use a CI/CD pipeline to deploy code from source control to your various environments, such as test, staging, and production. Use Azure Pipelines if you work with Azure DevOps. Use GitHub Actions for GitHub projects.

  • Integrate unit testing. Prioritize running and validating all unit tests within your pipeline before deployment to App Service. Incorporate code quality and coverage tools like SonarQube to achieve comprehensive testing coverage.

  • Adopt mocking frameworks. For testing that involves external endpoints, use mocking frameworks. These frameworks enable you to create simulated endpoints. They eliminate the need to configure real external endpoints and ensure uniform testing conditions across environments.

  • Perform security scans. Use static application security testing (SAST) to find security flaws and coding errors in your source code. Conduct software composition analysis (SCA) to examine non-Microsoft libraries and components for security risks. Integrate tools for these analyses into both GitHub and Azure DevOps.

Configure monitoring

Implement application and platform monitoring to enhance the operational excellence and performance efficiency of your web app. To implement monitoring, follow these recommendations:

  • Collect application telemetry. Use autoinstrumentation in Application Insights to collect application telemetry, such as request throughput, average request duration, errors, and dependency monitoring. You don't need to change any code to use this telemetry. Spring Boot registers several core metrics in Application Insights, like Java virtual machine (JVM), CPU, and Tomcat. Application Insights automatically collects from logging frameworks like Log4j and Logback.

    The reference implementation enables Application Insights via Terraform in the app service's app_settings configuration:

    app_settings = {
        APPLICATIONINSIGHTS_CONNECTION_STRING = var.app_insights_connection_string
        ApplicationInsightsAgent_EXTENSION_VERSION = "~3"
        ...
    }
    

    For more information, see the following articles:

  • Create custom application metrics. Implement code-based instrumentation to capture custom application telemetry by adding the Application Insights SDK and using its API.

  • Monitor the platform. Enable diagnostics for all supported services. Send diagnostics to the same destination as the application logs for correlation. Azure services create platform logs automatically but only store them when you enable diagnostics. Enable diagnostic settings for each service that supports diagnostics.

    The reference implementation uses Terraform to enable Azure diagnostics on supported services. The following Terraform code configures the diagnostic settings for the app service:

    # Configure diagnostic settings for app service
    resource "azurerm_monitor_diagnostic_setting" "app_service_diagnostic" {
      name                           = "app-service-diagnostic-settings"
      target_resource_id             = azurerm_linux_web_app.application.id
      log_analytics_workspace_id     = var.log_analytics_workspace_id
      #log_analytics_destination_type = "AzureDiagnostics"
    
      enabled_log {
        category_group = "allLogs"
    
      }
    
      metric {
        category = "AllMetrics"
        enabled  = true
      }
    }
    

Deploy the reference implementation

The reference implementation guides you through Contoso Fiber's simulated migration of an on-premises Java application to Azure. It also highlights required changes during the initial adoption phase.

The following architecture represents the final state of Contoso Fiber's Reliable Web App pattern implementation based on their goals.

Diagram that shows the architecture of the reference implementation.

Diagram that shows a reliable Java web application architecture on Azure that uses a hub-and-spoke network topology. Users access the app through Azure Front Door, which provides global load balancing, web application firewall, and DDoS protection. Azure Front Door routes traffic to App Service instances in a primary region and secondary region. Each App Service instance hosts a Java Spring Boot web app and is integrated with Application Insights for monitoring. Authentication and authorization are managed by Microsoft Entra ID. The app uses Azure Database for PostgreSQL flexible server for data storage, configured for high availability and read replicas, and Azure Managed Redis for distributed caching. Key Vault stores secrets, accessed via managed identities. Private Link secures connections between the app, database, and other PaaS resources. The architecture is deployed in a hub-and-spoke virtual network. The hub in the primary region contains Azure Firewall and Azure Bastion for network security and management, and the Key Vault private endpoint subnet. The spokes in the primary and secondary regions host the app and database. The subnets include the other private endpoints subnet, DevOps subnet, web app integration subnet, and web app private endpoint subnet. Diagnostic logs and metrics are sent to Azure Monitor and Log Analytics. A private DNS zones icon resides between the primary and secondary region. Arrows indicate data flow and relationships between components, illustrating a production-ready, scalable, and secure pattern for migrating a monolithic Java web app to Azure.

Download a Visio file of this architecture.

Next step