<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Anoush</title>
    <description>The latest articles on Forem by Anoush (@anoushnet).</description>
    <link>https://forem.com/anoushnet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F191310%2F76a99ea2-cc58-47c7-8ae7-3dc10ae89178.jpg</url>
      <title>Forem: Anoush</title>
      <link>https://forem.com/anoushnet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/anoushnet"/>
    <language>en</language>
    <item>
      <title>Scaling API Access with Azure API Management: From Manual to Self-Service</title>
      <dc:creator>Anoush</dc:creator>
      <pubDate>Sun, 04 Jan 2026 22:03:40 +0000</pubDate>
      <link>https://forem.com/anoushnet/scaling-api-access-with-azure-api-management-from-manual-to-self-service-3299</link>
      <guid>https://forem.com/anoushnet/scaling-api-access-with-azure-api-management-from-manual-to-self-service-3299</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Working on a healthcare application using .NET and Azure. The backend is .NET Core with SQL Server and Azure Functions on Azure Cloud. This application is a B2B product, where we need to expose our APIs to external clients and users who integrate with our services.&lt;/p&gt;

&lt;p&gt;We came out first with a traditional approach where each client would get custom API endpoints and we generate a GUID id (API key) for each client/user. We store the ids in our database and based on API key we authorize and authenticate the clients. But as we scaled up hundreds/thousands of facilities and users, this became unmanageable. Also, it is not secure to have the main API endpoint accessible to public. We needed a better way.&lt;/p&gt;

&lt;p&gt;This article describes how I architected our API access to be scalable, self-service and more secure. Basically, going from a manually configured API setup to an automated multi-tenant API gateway that serves thousands of users with zero manual configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Our application needed to serve hundreds or thousands of external users across multiple facilities. Each facility needed access to our APIs to exchange data. But here's the challenge: each client has access to different APIs and needed different parameters to push and pull their data.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facility A needs facilityId=5001&amp;amp;locationId=1001&lt;/li&gt;
&lt;li&gt;Facility B needs facilityId=5002&amp;amp;locationId=1002&lt;/li&gt;
&lt;li&gt;Facility C needs facilityId=5003&amp;amp;locationId=1003$specificParams=Ture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The traditional approach would be to create separate API id called API Key in the database and endpoints would access their data based on their API Key. Or worse, require each facility to pass their own parameters and trust they're sending the right ones. Neither scales. The first creates endpoint sprawl. The second creates security issues.&lt;/p&gt;

&lt;p&gt;We needed our users to sign up, set up their settings (set their facility, location and other parameters in UI) and choose their API endpoint that could serve everyone securely, with each user automatically getting their correct parameters without manual configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture and Setup
&lt;/h2&gt;

&lt;p&gt;We built our solution using three Azure services: Azure API Management (APIM), Azure Functions, and Azure Table Storage.&lt;br&gt;
APIM acts as our API gateway. It's the single entry point for all external users. Functions handle our business logic and subscription management. I used APIM to set up API policies. Table Storage holds each subscription's custom parameters.&lt;/p&gt;

&lt;p&gt;Here's how I set it up. In APIM, I created one API endpoint. This single endpoint serves all facilities. When a user calls this endpoint with their subscription key, APIM does all the heavy lifting automatically through a policy.&lt;/p&gt;

&lt;p&gt;I have a separate function that is fully dedicated to managing APIM subscriptions. I created two main APIs: one to manage subscription creation (POST /api/subscription/create) and one to retrieve subscription parameters (GET /api/subscription/{id}). The first one is called when users sign up. The second one is called by APIM's policy to get that user's parameters.&lt;/p&gt;

&lt;p&gt;In Table Storage, we store a simple table called ApiSubscriptions with three columns: SubscriptionId (the primary key), SubscriptionKey, and Parameters. When a user signs up, I store their parameters here as a query string like facilityId=5001&amp;amp;locationId=1001.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzy28dhtegn8g56wxy0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzy28dhtegn8g56wxy0i.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;When a user calls our API, here's what happens automatically:&lt;br&gt;
The facility sends a GET request to &lt;a href="https://api.example.com/data/ids" rel="noopener noreferrer"&gt;https://api.example.com/data/ids&lt;/a&gt; with their subscription key in the header: Ocp-Apim-Subscription-Key: abc123...&lt;br&gt;
APIM receives the request and validates the subscription key. If it's invalid, the request is rejected immediately.&lt;/p&gt;

&lt;p&gt;Once validated, APIM executes our policy. The policy does three things in sequence:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, it calls Azure AD to get an OAuth token. This token is needed to call our backend Function App securely. APIM makes a POST request to &lt;a href="https://login.microsoftonline.com/%7Btenant%7D/oauth2/v2.0/token" rel="noopener noreferrer"&gt;https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token&lt;/a&gt; with client credentials. Azure AD returns a bearer token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt;, APIM calls our Function App to get this subscription's parameters. It makes a GET request to /api/subscription/{subscriptionId} with the bearer token in the Authorization header. The Function App looks up the subscription in Table Storage and returns the parameters: facilityId=5001&amp;amp;locationId=1001.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third&lt;/strong&gt;, APIM appends these parameters to the main backend API call. It rewrites the URL from /data/ids to /data/ids?code={functionCode}&amp;amp;facilityId=5001&amp;amp;locationId=1001 and forwards the request to the Function App with the OAuth token.&lt;/p&gt;

&lt;p&gt;The Function App processes the request with parameters and returns the facility's requested data. APIM forwards this response back to the user.&lt;br&gt;
All of this happens in milliseconds. The facility doesn't know any of this is happening. They just send their subscription key (in the header of the call) and get their data back.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fji7omdsp2gv13dpd8nqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fji7omdsp2gv13dpd8nqb.png" alt="Azure architecture diagram showing APIM policy flow with 3 steps: OAuth token retrieval, subscription parameter lookup, and backend API call with authentication" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The diagram shows the complete flow from API Client through APIM to backend services with OAuth authentication and dynamic parameter injection.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The APIM Policy&lt;/strong&gt;&lt;br&gt;
The entire flow is controlled by an APIM policy. This is an XML-based configuration that runs for every API request. Here's what our policy looks like, broken down into the three main steps:&lt;br&gt;
&lt;strong&gt;Step 1: Get OAuth Token&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;send-request mode="new" response-variable-name="tokenResponse" timeout="10"&amp;gt;
    &amp;lt;set-url&amp;gt;https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token&amp;lt;/set-url&amp;gt;
    &amp;lt;set-method&amp;gt;POST&amp;lt;/set-method&amp;gt;
    &amp;lt;set-header name="Content-Type"&amp;gt;
        &amp;lt;value&amp;gt;application/x-www-form-urlencoded&amp;lt;/value&amp;gt;
    &amp;lt;/set-header&amp;gt;
    &amp;lt;set-body&amp;gt;grant_type=client_credentials&amp;amp;client_id={id}&amp;amp;client_secret={secret}&amp;amp;scope={id}/.default&amp;lt;/set-body&amp;gt;
&amp;lt;/send-request&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This calls Azure AD and gets back an access token. We store it in a variable called tokenResponse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Get Subscription Parameters&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;send-request mode="new" response-variable-name="subscriptionResponse" timeout="10"&amp;gt;
    &amp;lt;set-url&amp;gt;@($"https://{function-app}/api/subscription/{context.Subscription.Id}?code={code}")&amp;lt;/set-url&amp;gt;
    &amp;lt;set-method&amp;gt;GET&amp;lt;/set-method&amp;gt;
    &amp;lt;set-header name="Authorization"&amp;gt;
        &amp;lt;value&amp;gt;@("Bearer " + accessToken)&amp;lt;/value&amp;gt;
    &amp;lt;/set-header&amp;gt;
&amp;lt;/send-request&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This calls our Function App to get the parameters. Notice we're using context.Subscription.Id which is the unique subscription identifier in APIM. We send the OAuth token in the Authorization header.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Call Backend with Parameters&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;set-header name="Authorization"&amp;gt;
    &amp;lt;value&amp;gt;@("Bearer " + accessToken)&amp;lt;/value&amp;gt;
&amp;lt;/set-header&amp;gt;

&amp;lt;rewrite-uri template="@{
    string parameters = context.Variables["parameters"];
    return $"/api/data?code={code}&amp;amp;{parameters}";
}" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This appends the parameters to the backend call and sets the OAuth token in the Authorization header. The backend API receives the request with both authentication and the facility's specific parameters.&lt;/p&gt;

&lt;p&gt;That's the entire policy. Three steps that run automatically for every request.&lt;/p&gt;

&lt;p&gt;I also have other types of APIM Policies. For example, one that I use often is the rate-limit policy. That protects our APIs when there are a lot of calls during a short amount of time. This built-in policy will give "Too Many Requests" (HTTP 429) when that occurs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscription Management
&lt;/h2&gt;

&lt;p&gt;We built a Function App to handle subscription lifecycle. It has two main APIs:&lt;/p&gt;

&lt;p&gt;Create Subscription (POST /api/subscription/create)&lt;/p&gt;

&lt;p&gt;This function does four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates the user in APIM (if they don't exist)&lt;/li&gt;
&lt;li&gt;Creates the subscription in APIM for the selected product&lt;/li&gt;
&lt;li&gt;Stores the subscription parameters in Table Storage&lt;/li&gt;
&lt;li&gt;Returns the subscription key and ID to the user&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the data flow: user submits their email, name, product selection, and facility selection through our signup portal. The Function App creates an APIM user using their email as the identifier. Then it creates a subscription for that user to our API product. Then it stores the subscription ID and parameters (facilityId=X&amp;amp;locationId=Y) in Table Storage. Finally, it returns the subscription key to the user.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Get Subscription *&lt;/em&gt;(GET /api/subscription/{id})&lt;/p&gt;

&lt;p&gt;This function is called by the APIM policy. It takes a subscription ID, looks it up in Table Storage, and returns the parameters. Simple lookup operation.&lt;/p&gt;

&lt;p&gt;Both functions use OAuth authentication. Only APIM can call them because APIM uses Managed Identity to get a token from Azure AD.&lt;/p&gt;

&lt;p&gt;One important configuration: the Function App's Managed Identity must authenticate to the correct Azure AD tenant. I explicitly set the tenant ID in the DefaultAzureCredential to ensure it gets tokens from the right Azure AD instance. Without this, authentication fails with a tenant mismatch error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Portal
&lt;/h2&gt;

&lt;p&gt;I built two signup portals: one integrated into the APIM Developer Portal and one standalone static website hosted on Azure Storage.&lt;/p&gt;

&lt;p&gt;In the APIM Developer Portal, I created a custom page called "Create Subscription". Users can access this page, fill out a form with their email, name, product selection, facility selection, other settings and click "Create Subscription". The form calls our Function App's create endpoint, and the user immediately gets their subscription key displayed on screen.&lt;/p&gt;

&lt;p&gt;The standalone portal is a simple HTML page with JavaScript. It does the same thing but can be shared with external users who don't need to log into the APIM portal. We use this for quick onboarding by email invitations.&lt;/p&gt;

&lt;p&gt;Both portals load the facility list dynamically from another Function App endpoint (GET /api/facilities) which queries our database for active facilities. It also lets users choose their APIM Product or API Group.&lt;br&gt;
For the portals to call the Function App from the browser, I configured CORS in the APIM policy. I set allowed-origins to wildcard (*) for the POC. For production, this should be locked down to specific portal URLs only.&lt;/p&gt;

&lt;p&gt;When the user submits the form, JavaScript calls POST /api/subscription/create with the payload example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "email": "user@example.com",
  "name": "John Doe",
  "product": "example-apis",
  "facilityId": "5001",
  "locationId": "1001"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Function App does all the work and returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "subscriptionKey": "abc123...",
  "subscriptionId": "user-example-com-xyz789"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The portal displays this to the user with instructions on how to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This architecture solved our multi-tenant API access problem, without admin involvement and separates authentication and authorization using APIM. Each user gets their correct parameters automatically without manual configuration.&lt;/p&gt;

&lt;p&gt;The key components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIM handles routing and policy execution&lt;/li&gt;
&lt;li&gt;Azure Functions manage subscriptions and business logic&lt;/li&gt;
&lt;li&gt;Table Storage holds subscription parameters&lt;/li&gt;
&lt;li&gt;OAuth secures all communications&lt;/li&gt;
&lt;li&gt;APIM Policy ties it all together with three simple steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building B2B APIs that need to serve multiple tenants with different parameters, this pattern works. It's scalable, secure, and self-service.&lt;/p&gt;

&lt;p&gt;The full code and APIM policy are available in production. The system runs itself.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>azure</category>
      <category>api</category>
      <category>devops</category>
    </item>
    <item>
      <title>User Connectivity: Two Years in Production — Lessons Learned and New Patterns</title>
      <dc:creator>Anoush</dc:creator>
      <pubDate>Wed, 24 Dec 2025 06:23:54 +0000</pubDate>
      <link>https://forem.com/anoushnet/user-connectivity-two-years-in-production-lessons-learned-and-new-patterns-3fpf</link>
      <guid>https://forem.com/anoushnet/user-connectivity-two-years-in-production-lessons-learned-and-new-patterns-3fpf</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is a follow-up to my previous post on User Connectivity: A Real-time Web Solution for Online and Offline User Status. It has been almost two years since I first deployed my user connectivity solution to production. What started as an experiment has become a cornerstone of my architecture.&lt;br&gt;
Could I make it simpler? Yes. However, the time and effort for an upgrade is not worth it given other priorities. The system has been rock solid, and I have expanded its use to solve other challenges in my application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expanding the Pattern: Solving Database Deadlocks
&lt;/h2&gt;

&lt;p&gt;Recently, I encountered complex database deadlock issues caused by several API processes. After analyzing the problem, I recognized an opportunity to apply my event-driven architecture. By separating out specific tasks and processing them asynchronously, I eliminated the deadlocks entirely.&lt;br&gt;
This led me to create a centralized service I call the Event Distributor.  An Azure Function App responsible solely for listening to Redis expiration events and forwarding them to an Azure Event Grid queue. Multiple Azure Function App workers then process events from this queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event Distributor Architecture
&lt;/h2&gt;

&lt;p&gt;The goal of the Event Distributor is simple: one central service that listens to Redis expiration events and nothing else.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw956yvzss27amig3nkc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw956yvzss27amig3nkc2.png" alt=" " width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This separation provides several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traceability&lt;/strong&gt;: All events pass through a single point, making it easier to trace issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging&lt;/strong&gt;: Every received event is logged to an Azure Cosmos DB with its status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Worker functions can scale independently based on queue depth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: A single, well-monitored listener reduces the risk of missed events.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Observations: Azure Services
&lt;/h2&gt;

&lt;p&gt;After two years of running this architecture, I have two critical observations for anyone implementing a similar solution with Azure services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Use Premium Plan for Redis Listeners&lt;/strong&gt;&lt;br&gt;
If your Azure Function App uses the RedisPubSubTrigger to listen for Redis expiration events, you must use the Premium plan. The Consumption plan is not reliable for this use case.&lt;/p&gt;

&lt;p&gt;Why? The Consumption plan has cold start delays and limited connection lifetimes. When the function app is idle, it can go to sleep and miss events from Redis. With the Premium plan, your function app is always on with long-lived connections — no cold starts, no missed events. I have not seen a single lost event since moving to Premium.&lt;/p&gt;

&lt;p&gt;You may ask: how did the original user connectivity solution work on the Consumption plan?&lt;br&gt;
The answer is traffic volume. Even in idle mode, my application generates 170–200 heartbeat events per second. During peak usage, this increases 6–10 times. The function app never goes to sleep because of this constant traffic. Additionally, my Heartbeat function app includes both the RedisPubSubTrigger and HTTP triggers, keeping it active.&lt;br&gt;
For a dedicated Event Distributor that only listens to Redis expiration events, the Premium plan is essential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Azure Managed Redis Limitation&lt;/strong&gt;&lt;br&gt;
Azure has introduced a new Redis service called Azure Managed Redis (formerly Redis Enterprise). This service runs on a clustered architecture with multiple Redis servers under the hood.&lt;/p&gt;

&lt;p&gt;However, there is a significant limitation: these servers cannot be fully configured to send expiration events reliably. When the underlying servers restart or cycle, they lose their configuration and stop sending expiration events.&lt;/p&gt;

&lt;p&gt;I have reported this issue to Microsoft. They confirmed it is on their backlog, but as of today, there is no solution. If your architecture depends on Redis key expiration events, be aware of this limitation before migrating to Azure Managed Redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;My event-driven architecture has proven itself over two years of production use. It solved my original user connectivity requirements and has since helped me address complex database deadlock issues. The key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the Premium plan for Redis listeners&lt;/li&gt;
&lt;li&gt;Be cautious with Azure Managed Redis if you rely on expiration events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this helps others take advantage of this architecture.&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>eventdriven</category>
      <category>softwareengineering</category>
      <category>architecture</category>
    </item>
    <item>
      <title>User Connectivity: A Real-time Web Solution for Online and Offline User Status</title>
      <dc:creator>Anoush</dc:creator>
      <pubDate>Tue, 06 Feb 2024 06:46:08 +0000</pubDate>
      <link>https://forem.com/anoushnet/user-connectivity-a-real-time-web-solution-for-online-and-offline-user-status-3ll2</link>
      <guid>https://forem.com/anoushnet/user-connectivity-a-real-time-web-solution-for-online-and-offline-user-status-3ll2</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction:&lt;/strong&gt;&lt;br&gt;
This post explores user connectivity in a web application, focusing on displaying the real-time connectivity status of facilities. We'll discuss how users are associated with facilities, walk through our setup, communication methods with connected sessions, and our approach to ensuring a stable and reliable solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connectivity Status:&lt;/strong&gt;&lt;br&gt;
In our context, connectivity status refers to whether a facility is online or offline. This status indicates whether one or multiple users associated with a facility are actively logged into our application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application Setup and Details:&lt;/strong&gt;&lt;br&gt;
Our application operates in real-time, using sessions associated with users. Users are linked to specific facilities, and each facility is associated with a distinct region or county.&lt;br&gt;
The core of our real-time functionality involves notifying all relevant sessions/users about the facility's connectivity status in real-time, delivered to the user's browser using SignalR (Web Sockets protocol) event messages.&lt;br&gt;
We host our application on Microsoft Azure Cloud, utilizing the following technology stack and &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure Cloud Services:&lt;/strong&gt;&lt;br&gt;
Technology Stack:&lt;br&gt;
Front-end: JavaScript/Angular&lt;br&gt;
Back-end: .Net Core&lt;br&gt;
Database: Azure SQL Server&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure Services:&lt;/strong&gt;&lt;br&gt;
SignalR&lt;br&gt;
Azure Event Hub&lt;br&gt;
Redis Cache&lt;br&gt;
Azure Functions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
Now, let's dive into the specifics of our solution.&lt;br&gt;
To establish real-time user connectivity, our front-end application regularly sends heartbeat signals through simple Post API calls every 30 seconds. The heartbeat events are stored in an Azure Event Hub Queue, which can handle millions of events per second.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6ae7qqtwh3ptlukwwln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6ae7qqtwh3ptlukwwln.png" alt="Heartbeat API Calls" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heartbeat Event Process:&lt;/strong&gt;&lt;br&gt;
We utilize a worker service called the Heartbeat Monitor (implemented as Azure Functions) to read and store heartbeat events in Redis Cache. The events are stored in two lists: a Redis expiring list called Session list and another list called Session Value list, containing heartbeat event values.&lt;br&gt;
Each time a heartbeat event is read, it is fetched from the Session list. If it does not exist, the session element is transactionally created in both lists to ensure synchronization. If the session already exists, the Session End Time is set to the expiration time in the Session Value list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffv3jqj0ky1ic13le3sy8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffv3jqj0ky1ic13le3sy8.png" alt="Heartbeat Monitor Process" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expired Session Callback:&lt;/strong&gt;&lt;br&gt;
To manage session expiration, we listen to Redis expired keys and execute a function that processes the session and sets it to offline with a timestamp. This is done by configuring Redis to send events and listening to a specific event: __keyevent@0:expired.&lt;/p&gt;

&lt;p&gt;However, Microsoft has introduced a new Azure Function Trigger, &lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cache-trigger-redispubsub?tabs=isolated-process%2Cnode-v3%2Cpython-v1&amp;amp;pivots=programming-language-csharp" rel="noopener noreferrer"&gt;"RedisPubSubTrigger"&lt;/a&gt;, which captures the expiration (__keyevent@0:expired) of a session more efficiently.&lt;/p&gt;

&lt;p&gt;When a session expires, this listner function sets the session's offline timestamp in the session value list, which is set to null while session in online.&lt;/p&gt;

&lt;p&gt;By listening to Redis expired keys, we execute function that process the session and set the session to be offline with timestamp.  This was originally done by configuring the Redis to send events and listening to specific event "__keyevent@0:expired".  However, Microsoft has new Azure Function Trigger &lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cache-trigger-redispubsub?tabs=isolated-process%2Cnode-v3%2Cpython-v1&amp;amp;pivots=programming-language-csharp" rel="noopener noreferrer"&gt;"RedisPubSubTrigger"&lt;/a&gt; that capture the expire session.  The session offline timestamp been set in the session value list, there after.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session Monitor:&lt;/strong&gt;&lt;br&gt;
Another timer-based Azure Functions worker service runs every minute, processing each session in the Session Value list, associating sessions with facilities. After processing all sessions, the Session Monitor updates facility connectivity status, stores data in its private database, and removes offline sessions from the Session Value list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Process Facility Session:&lt;/strong&gt;&lt;br&gt;
Moving from the Session Monitor's role, let's explore the 'Process Facility Session' step. Here, we update facility connectivity status and perform tasks for seamless user connectivity. The Session Monitor submits events to proper users or regions/counties, updates facility data in its database, and updates the facility status in the core database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewv7pej7jbceaip1tx8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewv7pej7jbceaip1tx8d.png" alt="Session Monitor Process" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conclusion:&lt;br&gt;
The facility session process is highly efficient, processing only the updated facilities whose connectivity status has changed. It summarizes all sessions to the facility, avoiding the need to send events per session and reducing processing per browser connection.&lt;br&gt;
Using Redis expiration keys is reliable and stable, streamlining functionality and code by eliminating the need to individually process each session in the list to determine its online or offline status.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (December 2024):&lt;/strong&gt;&lt;br&gt;
This architecture has been running successfully in production for over two years. I have published a follow-up article covering lessons learned, a new Event Distributor pattern, and important Azure service considerations: User Connectivity: Two Years in Production — Lessons Learned and New Patterns&lt;/p&gt;

</description>
      <category>azure</category>
      <category>onlineoffline</category>
      <category>api</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Going Desktop to Mobile view by configuring the Angular router</title>
      <dc:creator>Anoush</dc:creator>
      <pubDate>Tue, 09 Jul 2019 03:22:01 +0000</pubDate>
      <link>https://forem.com/anoushnet/going-desktop-to-mobile-view-by-configuring-the-angular-router-4087</link>
      <guid>https://forem.com/anoushnet/going-desktop-to-mobile-view-by-configuring-the-angular-router-4087</guid>
      <description>&lt;p&gt;Working on an emergency application (with live data) using .net and angular.  The backend is .net with SQL server and SignalR on Azure.  This application is sales driven product, we came out first with desktop version and then mobile friendly version due to way the market and 90-80% of users are on desktop view.&lt;/p&gt;

&lt;p&gt;This article describes how we architect our application do be responsive.  Basically, going from an already existing desktop web application to responsive application.  When referring to word “mobile” in article, it is not a mobile native application or Progressive Web App, it is referring to responsive application on different tables and mobile devices.&lt;/p&gt;

&lt;h1&gt;
  
  
  Architecture and Setup
&lt;/h1&gt;

&lt;p&gt;We started by in our angular application under root folder “src”.  We created a folder called “mobile”.  We have the “app” folder for main desktop view, it can be renamed to “desktop”.  This gave us a whole new place to have our mobile code and not been in the same folders with all other code, it is more organized, clean and separated from all other code.&lt;/p&gt;

&lt;p&gt;In app router module “AppRoutingModule”, when application get loaded.  We load an array of all paths of the application, from “app” and “mobile” folders.  When application knows the size of window “window.innerWidth” it will modify the path array and rests the route configuration by calling "this.route.resetConfig()".  Here we also listen to “window:resize” in the main component of the application for window resizing, and the router path gets modified and then router configuration gets reloaded.  So, user can go from desktop to mobile view and vice and versa.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;desktopPreloadModules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/modules/dashboard/dashboard.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DashboardModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;debug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/modules/debug/debug.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DebugModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mobilePreloadModules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mobile/modules/dashboard/dashboard.mobile.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DashboardMobileModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;debug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mobile/modules/debug/debug.mobile.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DebugMobileModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appRoutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LoginComponent&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MainComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;canActivate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AuthGuard&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppDataResolver&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;desktopPreloadModules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;mobilePreloadModules&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;redirectTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pathMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LoginComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RouterModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appRoutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;preloadingStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SelectiveStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;enableTracing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;scrollPositionRestoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SelectiveStrategy&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RouterModule&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppRoutingModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also CSS files that styling changes depending on the window size, we used media query for that, by calling:&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;@media only screen and (max-width: ####px)&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;In our application we also have some dynamic dialog boxes, we also override those components by reloading them either mobile view or desktop view.&lt;/p&gt;

&lt;p&gt;After route is change the all components (that is in the current view) needs to be reloaded.  Here, we solve this by simply navigating to and empty path this basically reloads all current component in the DOM (it is not refresh from server.)&lt;/p&gt;

&lt;h1&gt;
  
  
  Mobile Implementation
&lt;/h1&gt;

&lt;p&gt;In the mobile for with similar path tree as desktop “app” folder, by using OOP inheritance, polymorphism and overriding methods.  We could access all of the functionalities from desktop services and components and reused the code, by extending from same components.  Following the open/closed principle.  For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-mobile-component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./my.mobile.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyMobileComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MyDesktopComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case mobile functionality is different than desktop, we override the desktop method in the mobile folder.&lt;/p&gt;

&lt;p&gt;In our application which is medium to advanced application complexity.  There is only one place that it is checking if application is mobile or desktop and that is in the “AppRoutingModule” there no “if (mobile) do this else do that” outside the router.  We follow and stick with this pattern due to the fact that the application could be very messy.  If we have a mobile specific component or function with extending the class from desktop and override or add mobile specific functions or view.  Following the single responsibility principle.&lt;/p&gt;

&lt;h1&gt;
  
  
  Pros
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Clean and organized code.&lt;/li&gt;
&lt;li&gt;Easy to implement the mobile view application.&lt;/li&gt;
&lt;li&gt;Reuse of the code, this saves us lots of time to impalement and test.  The desktop code is already in production and been used by users, we know if works.  So, we are confident that code works.&lt;/li&gt;
&lt;li&gt;Follows the single responsibility principle&lt;/li&gt;
&lt;li&gt;Follows the open/closed principle&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Cons
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;If we want to override a desktop behavior or functionality we will need to extend it in mobile.  This can give a little complexity to some deeper functionalities.  However, doable by refactoring and using SOLID principles.&lt;/li&gt;
&lt;li&gt;Can easily get a messy code if we breaking the pattern if someone writes “if (mobile) do this else do that” way or some other way.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>architecture</category>
      <category>responsive</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
