Introduction
A middleware is a software service that glues together multiple services. In today's business needs, multiple software services and technologies need to work together and communicate with each other. It is not necessary that these distributed software services are compatible with each other and will be able to communicate.
Example Business Case
We have to develop a software service in which we have geo-coordinates of a location and we need to get weather information of the city based on those coordinates. We have a system X that needs to communicate with another system Y. These are distributed systems. System X has information about geo coordinates and system Y will store weather information of the city based on those coordinates.
Solution
We will develop a middleware between system X and system Y.
Middleware Architecture
- System X will send geo coordinates to the receiver service (Http Triggered Azure Function) of the middleware in JSON format.
- Receiver service will call reverse geocoder API and will extract city name from the response and finally sends the city name to the Service Bus queue.
- Sender service will receive city name from service and call weather API and send weather data to System Y (Service Bus Queue Triggered Function).
- System Y will receive weather information from the sender service and store it (For sake of simplicity, we will log the information at Sender Service).
Prerequisites
- Microsoft Azure Subscription.
- Deployed Service Bus resource on Microsoft Azure Portal.
- Postman for testing
- Visual Studio 2019
- .NET Core 3.1
APIs Used
- Open Weather Map
- Big Data Cloud Reverse Geocode
APIs Formats
Request From System X
- {
- "latitude":51.5074,
- "longitude":0.1278
- }
Reverse Geocode API Response
- {
- "latitude": 37.42158889770508,
- "longitude": -122.08370208740234,
- "plusCode": "849VCWC8+JG",
- "localityLanguageRequested": "en",
- "continent": "North America",
- "continentCode": "NA",
- "countryName": "United States of America",
- "countryCode": "US",
- "principalSubdivision": "California",
- "principalSubdivisionCode": "US-CA",
- "city": "Mountain View",
- "locality": "Googleplex",
- "postcode": "94043",
- "localityInfo": {
- "administrative": [
- {
- "order": 1,
- "adminLevel": 2,
- "name": "United States of America",
- "description": "country in North America",
- "isoName": "United States of America (the)",
- "isoCode": "US",
- "wikidataId": "Q30",
- "geonameId": 6252001
- },
- {
- "order": 4,
- "adminLevel": 4,
- "name": "California",
- "description": "state of the United States of America",
- "isoName": "California",
- "isoCode": "US-CA",
- "wikidataId": "Q99",
- "geonameId": 5332921
- },
- {
- "order": 6,
- "adminLevel": 6,
- "name": "Santa Clara County",
- "description": "county in California, United States",
- "wikidataId": "Q110739",
- "geonameId": 5393021
- },
- {
- "order": 7,
- "adminLevel": 8,
- "name": "Mountain View",
- "description": "city"
- }
- ],
- "informative": [
- {
- "order": 0,
- "name": "North America",
- "description": "continent on the Earth's northwestern quadrant",
- "isoCode": "NA",
- "wikidataId": "Q49",
- "geonameId": 6255149
- },
- {
- "order": 2,
- "name": "contiguous United States",
- "description": "48 states of the United States apart from Alaska and Hawaii",
- "wikidataId": "Q578170"
- },
- {
- "order": 3,
- "name": "America/Los_Angeles Timezone",
- "description": "timezone"
- },
- {
- "order": 5,
- "name": "Pacific Coast Ranges",
- "description": "A series of mountain ranges along the Pacific coast of North America",
- "wikidataId": "Q660304"
- },
- {
- "order": 8,
- "name": "94043",
- "description": "postal code"
- },
- {
- "order": 9,
- "name": "Googleplex",
- "description": "building complex in California, United States",
- "wikidataId": "Q694178",
- "geonameId": 6301403
- }
- ]
- }
- }
Open Weather Map API Response
- {
- "coord": {
- "lon": -0.13,
- "lat": 51.51
- },
- "weather": [
- {
- "id": 501,
- "main": "Rain",
- "description": "moderate rain",
- "icon": "10d"
- }
- ],
- "base": "stations",
- "main": {
- "temp": 287.47,
- "feels_like": 287.02,
- "temp_min": 287.04,
- "temp_max": 287.59,
- "pressure": 991,
- "humidity": 100
- },
- "visibility": 10000,
- "wind": {
- "speed": 2.6,
- "deg": 80
- },
- "rain": {
- "1h": 1.15
- },
- "clouds": {
- "all": 100
- },
- "dt": 1603273771,
- "sys": {
- "type": 1,
- "id": 1414,
- "country": "GB",
- "sunrise": 1603262110,
- "sunset": 1603299281
- },
- "timezone": 3600,
- "id": 2643743,
- "name": "London",
- "cod": 200
- }
Receiver Service
Receiver Service is implemented as Azure Function with Http trigger. We also have to create classes for request from System X and for a response from Geocode API. We need classes for JSON requests and responses, so we can deserialize them into C# objects and access information in them. It is also helpful for mapping data to another object.
GeoRequest Class
Sender Service
Sender Service is also implemented as Azure Function with Service Bus Queue trigger. As soon as there's a message in Service Bus Queue, the Sender Service will be executed.
WeatherResponse
- public class WeatherResponse
- {
- public Coord coord { get; set; }
- public Weather[] weather { get; set; }
- public string _base { get; set; }
- public Main main { get; set; }
- public int visibility { get; set; }
- public Wind wind { get; set; }
- public Rain rain { get; set; }
- public Clouds clouds { get; set; }
- public int dt { get; set; }
- public Sys sys { get; set; }
- public int timezone { get; set; }
- public int id { get; set; }
- public string name { get; set; }
- public int cod { get; set; }
- }
-
- public class Coord
- {
- public float lon { get; set; }
- public float lat { get; set; }
- }
-
- public class Main
- {
- public float temp { get; set; }
- public float feels_like { get; set; }
- public float temp_min { get; set; }
- public float temp_max { get; set; }
- public int pressure { get; set; }
- public int humidity { get; set; }
- }
-
- public class Wind
- {
- public float speed { get; set; }
- public int deg { get; set; }
- }
-
- public class Rain
- {
- public float _1h { get; set; }
- }
-
- public class Clouds
- {
- public int all { get; set; }
- }
-
- public class Sys
- {
- public int type { get; set; }
- public int id { get; set; }
- public string country { get; set; }
- public int sunrise { get; set; }
- public int sunset { get; set; }
- }
-
- public class Weather
- {
- public int id { get; set; }
- public string main { get; set; }
- public string description { get; set; }
- public string icon { get; set; }
- }
Sender Function
- using System;
- using System.Net.Http;
- using Microsoft.Azure.WebJobs;
- using Microsoft.Azure.WebJobs.Host;
- using Microsoft.Extensions.Logging;
- using Newtonsoft.Json;
-
- namespace Sender
- {
- public static class SenderFunction
- {
- [FunctionName("Sender")]
- public static async void Run([ServiceBusTrigger("Queue_Name", Connection = "Service_Bus_Connection")]string city, ILogger log)
- {
-
- var client = new HttpClient();
- var request = new HttpRequestMessage
- {
- Method = HttpMethod.Get,
-
- RequestUri = new Uri($"https://rapidapi.p.rapidapi.com/weather?q={city}"),
- Headers =
- {
- { "x-rapidapi-host", "community-open-weather-map.p.rapidapi.com" },
- { "x-rapidapi-key", "API_KEY" },
- },
- };
-
-
- WeatherResponse weatherResponse;
- using (var response = await client.SendAsync(request))
- {
- response.EnsureSuccessStatusCode();
- var body = await response.Content.ReadAsStringAsync();
-
- weatherResponse = JsonConvert.DeserializeObject<WeatherResponse>(body);
- }
-
-
- log.LogInformation($"Temperature in {city} is {weatherResponse.main.temp}K");
- }
- }
- }
Testing
Run the functions
Using Postman
Open Postman and make POST Http Request to Receiver Function endpoint.
Verify Results
Conclusion
As shown above, we can create middleware with Microsoft Azure Service Bus and Function. We can also add many other features to it such as Azure Application Insights for logging and performance monitoring, CosmosDB or Blob Storage for archiving data, we can also use Service Bus Topics if our scenario requires Publisher/Subscriber message model instead of simple queues. We can also expand our Receiver Service and Sender Service to cater to more communication scenarios. We can also do message transformation from System X to System Y such that message format is compatible with System Y using this approach.