{"id":20638062,"url":"https://github.com/imtiajahammad/ecomm","last_synced_at":"2025-07-18T00:07:03.943Z","repository":{"id":240856536,"uuid":"803604276","full_name":"imtiajahammad/Ecomm","owner":"imtiajahammad","description":null,"archived":false,"fork":false,"pushed_at":"2024-07-01T19:27:35.000Z","size":925,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-17T08:17:11.292Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/imtiajahammad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-21T03:36:45.000Z","updated_at":"2024-07-01T19:27:38.000Z","dependencies_parsed_at":"2024-05-21T05:41:30.614Z","dependency_job_id":"9ae7cf0d-2b05-47e5-9254-3e06e66d8e2d","html_url":"https://github.com/imtiajahammad/Ecomm","commit_stats":null,"previous_names":["imtiajahammad/ecomm"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imtiajahammad%2FEcomm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imtiajahammad%2FEcomm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imtiajahammad%2FEcomm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imtiajahammad%2FEcomm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/imtiajahammad","download_url":"https://codeload.github.com/imtiajahammad/Ecomm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242667474,"owners_count":20166303,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-16T15:16:47.909Z","updated_at":"2025-03-09T08:41:43.395Z","avatar_url":"https://github.com/imtiajahammad.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\nMicroservices:\n\n* What is Monolith?\n\t- A monolithic application  is an application where every part of a product is part of same service or web application\n\t- And usually the data for entire application is in a single data store \n* Issues with Monolithic Application\n\t- Source code management in source control (Merge nightmares)\n\t- Extremely big team, hence management nightmare\n\t- Code and Database deployments and rollbacks are a nightmare.\n* What is Microservice?\n\t- Microservices are smaller single responsibility services\n\t- It does one thing and one thing only\n\t- It has clear boundary\n\t- Usually they own their data and the data store\n\t- The logic and data of a single responsibility microservice should not leak outside of it\n* What about the cost of deployment?\n\t- Cost with IIS\n\t\t- If all services are deployed in same Windows machine in an IIS, if IIS has an issue all app will fail\n\t\t- If you want multiple IIS and boxes, there is significantly higher cost associated with it\n\t- Cost of Database\n\t\t- Having so many databases, especially if we are using SQL server, it is not at all cost effective\n\t\t- It is important to have databases isolated from each other\n* What is Containers?\n\t- Containers are used for packaging software and all its dependencies into a standard unit for development, deployment and shipment\n\t- They are immutable\n\t- Containers are defined as images, which becomes containers on runtime\n\t- Docker is the most popular container implementation \n* Difference between containers and virtual machines?\n\t- Containers\n\t\t- Lighweight (in MB sizes)\n\t\t- Faster to start\n\t\t- Best suited for Microservices\n\t\t- Resource utilization can be optimized to use most out of a machine\n\t\t- Less secure across containers\n\t- Virtual Machines\n\t\t- Heavy (can go in GB sizes)\n\t\t- Slower to start\n\t\t- Can be used, but not cost effective\n\t\t- Suboptimal resource optimization\n\t\t- Secure across VMs\n* How is containers helping me?\n\t- .Net Core applications can run on containers. In fact, .Net Core was built with containers in mind\n\t- Running in containers gives all the advantages of containers, including savings in cost and efficiency\n\t- Database like Postgres can be easily run in containers and have isolations we want\n* When should I use Microservice?\n\t- Start with a monolithic application if you are not sure\n\t- The main decider of going to microservice is your agility of deployment. \n\t- Think about which functionality makes sense breaking up for ease of deployment\n\t- If you are going with microservices, Docker is your best friend\n* Tools needed\n\t- Docker for packaging and deployment\n\t- When you use Docker, there comes the question of how to scale out or scale down. Hence you need an orchestration engine, Kubernetes is the most popular and open source container orchestration engine\n\t- Orchestration engines are not necessary to start\n* Microservice Communication?\n\t- Most popular is REST API. (GRPC is catching up quickly)\n\t- Message based communication (Queue or Pub/Sub), which works very well when you want to offload work to somme background microservices. And the response is not time sensitive.\n* How do I convert my monolith to Microservices?\n\t- Bring out the most easily separable component from the Monolith first\n\t- If you using SQL Server (alike), your first option might be to make a service responsible for table set instead of an entire database\n\t- If you are in cloud, use cloud native managed databases like DynamoDB in AWS or Cosmos DB in Azure\n* What about reporst?\n\t- When data is available across multiple databases, getting a consolidated view of the data or report is hard.\n\t- You can either use data aggregation by calling multiple services\n\t- Or you can create a data stream for all the data and have an aggregator create a read-only view of the data\n* Some Important notes\n\t- When you start building your microservices, think about resilience during design\n\t- Establish the communication pattern you have to use between your microservices based on your response needs.\n\t- Logging and monitoring can not be an afterthought. Cloud based centralised logging is extremely critical. Also passing a correlation log id across microservices would save you lot of debug time in future\n\t- Don’t build a distributed monolith. Meaning, keep complete decouple between the microservices. \n---\n1. Create a blank solution in Visual Studio\n2. Add a new API project **OrderService** with **net6.0** framework in the solution\n3. Delete the existing Controller **WeatherForecastController** controller and **WeatherForecast** class\n4. Create a new controller **OrderController** with read/write action\n```\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Mvc;\n\n// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860\n\nnamespace OrderService.Controllers\n{\n    [ApiController]\n    [Route(\"api/[controller]\")]\n    public class OrderController : Controller\n    {\n        private readonly IOrderDetailsProvider _orderDetailsProvider;\n\n        public OrderController(IOrderDetailsProvider orderDetailsProvider)\n        {\n            _orderDetailsProvider = orderDetailsProvider;\n        }\n        // GET: api/values\n        [HttpGet]\n        public IEnumerable\u003cOrderDetail\u003e Get()\n        {\n            return _orderDetailsProvider.Get();\n        }\n\n        // GET api/values/5\n        [HttpGet(\"{id}\")]\n        public string Get(int id)\n        {\n            return \"value\";\n        }\n\n        // POST api/values\n        [HttpPost]\n        public void Post([FromBody]string value)\n        {\n        }\n\n        // PUT api/values/5\n        [HttpPut(\"{id}\")]\n        public void Put(int id, [FromBody]string value)\n        {\n        }\n\n        // DELETE api/values/5\n        [HttpDelete(\"{id}\")]\n        public void Delete(int id)\n        {\n        }\n    }\n}\n\n\n```\n5. Create new interface for order with the name **IOrderDetailsProvider**\n```\npublic interface IOrderDetailsProvider\n{\n\tOrderDetail[] Get();\n}\n```\n6. Create new class named **OrderDetailsProvider** and implement the **IOrderDetailsProvider**\n```\n\tpublic class OrderDetailsProvider : IOrderDetailsProvider\n\t{\n\t\tprivate readonly string _connectionString;\n\t\tpublic OrderDetailsProvider(string connectionString)\n\t\t{\n\t\t\t_connectionString = connectionString;\n\t\t}\n\t\tpublic OrderDetail[] Get()\n\t\t{\n\t\t\tusing var connection = new SqlConnection(_connectionString);\n\t\t\treturn connection.Query\u003cOrderDetail\u003e(@\"SELECT o.UserName AS [User], od.ProductName AS Name, od.Quantity  FROM [Order] o\n                                            JOIN [OrderDetail] od on o.Id = od.OrderId\")\n\t\t\t\t.ToArray();\n\t\t}\n\t}\n```\n7. Create new class **OrderDetail** for model class\n```\npublic class OrderDetail\n{\n\tpublic string User { get; set; }\n\tpublic string Name { get; set; }\n\tpublic int Quantity { get; set; }\n}\n```\n8. Install **dapper** package for dbconnection\n9. Install **System.Data.SqlClient** for SqlConnection\n10. Add connectionString into **appSettings.json**\n```\n\"ConnectionStrings\": {\n    \"DefaultConnection\": \"Server=localhost;Database=ecom;User=sa;Password=Docker@123;\"\n}\n```\n11. Add the injection in the program file for **IOrderDetailsProvider**\n```\nvar connectionString = builder.Configuration[\"ConnectionStrings\"];\nvar connectionString2 = builder.Configuration.GetConnectionString(\"DefaultConnection\");\nbuilder.Services.AddSingleton\u003cIOrderDetailsProvider\u003e(sp =\u003e new OrderDetailsProvider(connectionString2));\n```\n12. Go to the controllers and update the **OrderController** \n```\nprivate readonly OrderDetailsProvider _orderDetailsProvider;\n\npublic OrderController(OrderDetailsProvider orderDetailsProvider)\n{\n        _orderDetailsProvider = orderDetailsProvider;\n}\n// GET: api/values\n[HttpGet]\npublic IEnumerable\u003cOrderDetail\u003e Get()\n}\nreturn _orderDetailsProvider.Get();\n}\n```\n13. Let’s create a database named **ecom** and some tables with some data so that the api can fetch some data\n```\nCREATE TABLE [dbo].[Inventory] (\n    [Id]        INT           IDENTITY (1, 1) NOT NULL,\n    [Name]      NVARCHAR (50) NULL,\n    [Quantity]  INT           NOT NULL,\n    [ProductId] INT           NULL,\n    CONSTRAINT [PK_Inventory] PRIMARY KEY CLUSTERED ([Id] ASC),\n    CONSTRAINT [FK_Inventory_Product] FOREIGN KEY ([ProductId]) REFERENCES [dbo].[Product] ([Id])\n);\n```\n```\nCREATE TABLE [dbo].[Order] (\n    [Id]          INT      IDENTITY (1, 1) NOT NULL,\n    [UserId]      INT      NOT NULL,\n    [UpdatedTime] DATETIME NOT NULL,\n    CONSTRAINT [PK_Order] PRIMARY KEY CLUSTERED ([Id] ASC),\n    CONSTRAINT [FK_Order_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])\n);\n```\n```\nCREATE TABLE [dbo].[OrderDetails] (\n    [OrderId]   INT NOT NULL,\n    [ProductId] INT NOT NULL,\n    [Quantity]  INT NULL,\n    CONSTRAINT [FK_OrderDetails_Order] FOREIGN KEY ([OrderId]) REFERENCES [dbo].[Order] ([Id]),\n    CONSTRAINT [FK_OrderDetails_Product] FOREIGN KEY ([ProductId]) REFERENCES [dbo].[Product] ([Id])\n);\n```\n```\nCREATE TABLE [dbo].[Product] (\n    [Id]          INT            IDENTITY (1, 1) NOT NULL,\n    [Name]        NVARCHAR (50)  NOT NULL,\n    [Description] NVARCHAR (MAX) NULL,\n    [Type]        NVARCHAR (50)  NOT NULL,\n    CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED ([Id] ASC)\n);\n```\n```\nCREATE TABLE [dbo].[User] (\n    [Id]      INT            IDENTITY (1, 1) NOT NULL,\n    [Name]    NVARCHAR (50)  NOT NULL,\n    [Address] NVARCHAR (MAX) NULL,\n    CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC)\n);\n```\n14. Now let’s run the **orderservice** and test the get API\n15. Add a **README.md** file in the solution directory\n```\ntouch README.md\n```\n16.   Add a **gitignore** file in the solution directory\n```\ndotnet new gitignore\n```\n---\n17. Now lets create a **MVC** applicaton where we can consume the **OrderService**. Lets Create a **MVC** project with **dotnet6.0** named **ClientPlatform**\n18. Add a new folder **DataAccess** and add a new class called **OrderDetailsProvider**\n```\n\tpublic class OrderDetailsProvider : IOrderDetailsProvider\n    {\n        private readonly IHttpClientFactory _httpClientFactory;\n        private readonly ILogger\u003cOrderDetailsProvider\u003e _logger;\n\n\n        public OrderDetailsProvider(IHttpClientFactory httpClientFactory, ILogger\u003cOrderDetailsProvider\u003e logger)\n        {\n            _httpClientFactory = httpClientFactory;\n            _logger = logger;\n        }\n        public async Task\u003cOrderDetail[]\u003e Get()\n        {\n            try\n            {\n                using var client = _httpClientFactory.CreateClient(\"order\");\n                var response = await client.GetAsync(\"/api/order\");\n                var data = await response.Content.ReadAsStringAsync();\n                return JsonSerializer.Deserialize\u003cOrderDetail[]\u003e(data);\n            }\n            catch(Exception ex)\n            {\n                _logger.LogError($\"Error getting order detials, ex:{ex}\");\n                return Array.Empty\u003cOrderDetail\u003e();\n            }\n        }\n    }\n```\n19. Create a folder for models **Models** and add a class **OrderDetail**\n```\n\tpublic class OrderDetail\n\t{\n\n        [JsonPropertyName(\"User\")]\n        public string User { get; set; }\n        [JsonPropertyName(\"Name\")]\n        public string Name { get; set; }\n        [JsonPropertyName(\"Quantity\")]\n        public int Quantity { get; set; }\n    }\n```\n20. Add injection for OrderDetailsProvider with HttpClientFacotry in the program file\n```\nbuilder.Services.AddSingleton\u003cIOrderDetailsProvider, OrderDetailsProvider\u003e();\n\nbuilder.Services.AddHttpClient(\"order\", config =\u003e\n    config.BaseAddress = new System.Uri(\"https://localhost:7177/\"));\n```\n21. Add a new class called **OrderDetailsProvider** in **DataAccess** folder\n```\npublic interface IOrderDetailsProvider\n{\n    Task\u003cOrderDetail[]\u003e Get();\n}\n```\n22. Add the method for the **HomeController** \n```\npublic class HomeController : Controller\n{\n    private readonly IOrderDetailsProvider _orderDetailsProvider;\n    private readonly ILogger\u003cHomeController\u003e _logger;\n\n    public HomeController(ILogger\u003cHomeController\u003e logger, IOrderDetailsProvider orderDetailsProvider)\n    {\n        _logger = logger;\n        _orderDetailsProvider = orderDetailsProvider;\n    }\n\n    public async Task\u003cIActionResult\u003e Index()\n    {\n        var orderDetails = await _orderDetailsProvider.Get();\n        return View(orderDetails);\n    }\n}\n```\n23. Edit the View **Index.cshtml**\n```\n@model ClientPlatform.Models.OrderDetail[]\n@{\n    ViewData[\"Title\"] = \"Home Page - Ecomm\";\n}\n\u003cstyle\u003e\n    table {\n        font-family: arial, sans-serif;\n        border-collapse: collapse;\n        width: 100%;\n    }\n\n    td, th {\n        border: 1px solid #dddddd;\n        text-align: left;\n        padding: 8px;\n    }\n\n    tr:nth-child(even) {\n        background-color: #dddddd;\n    }\n\u003c/style\u003e\n\u003cdiv class=\"text-center\"\u003e\n    \u003ch1 class=\"display-4\"\u003eWelcome\u003c/h1\u003e\n    \u003cp\u003eLearn about \u003ca href=\"https://docs.microsoft.com/aspnet/core\"\u003ebuilding Web apps with ASP.NET Core\u003c/a\u003e.\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv class=\"text-center\"\u003e\n    \u003ctable\u003e\n        \u003cthead\u003e\n            \u003ctr \u003e\n                \u003cth\u003eUser Name\u003c/th\u003e\n                \u003cth\u003eProduct Name\u003c/th\u003e\n                \u003cth\u003eQuantity\u003c/th\u003e\n            \u003c/tr\u003e\n        \u003c/thead\u003e\n        \u003ctbody\u003e\n            @foreach (var item in Model)\n            {\n                \u003ctr \u003e\n                    \u003ctd\u003e@item.User \u003c/td\u003e\n                    \u003ctd\u003e@item.Name \u003c/td\u003e\n                    \u003ctd\u003e@item.Quantity\u003c/td\u003e\n                \u003c/tr\u003e\n            }\n        \u003c/tbody\u003e\n    \u003c/table\u003e\n\u003c/div\u003e\n```\n24. If you check the query of fething the orderDetails, you might find that there is a joining with the table **Product** which is not recommended. We can avoid this scenario by adding a new column into **OrderDetails** table called **Name** and remove the joining with the **Product** table.\n```\nALTER TABLE [dbo].[OrderDetails]\n    ADD [ProductName] NVARCHAR (50) NULL;\n```\nAlso edit the existing data and add productNames.   \nUpdate the query in **OrderService** in **OrderDetailsProvider**\n```\npublic class OrderDetailsProvider : IOrderDetailsProvider\n    {\n        private readonly string _connectionString;\n        public OrderDetailsProvider(string connectionString)\n        {\n            _connectionString = connectionString;\n        }\n        public OrderDetail[] Get()\n        {\n            using var connection = new SqlConnection(_connectionString);\n\n           return connection.Query\u003cOrderDetail\u003e(@\"\n                                                    SELECT u.Name AS [USER], od.ProductName AS Name, od.Quantity    from [Order] o\n\t\t\t\t\t\t\t\t\t\t\t\t\tJOIN [OrderDetails] od ON o.Id = od.OrderId\n\t\t\t\t\t\t\t\t\t\t\t\t\tJOIN [User] u ON o.UserId = u.Id\"\n            )\n                .ToArray();\n        }\n    }\n```\n25. Similarly, with 24, fething **Orders** should not have a joining query with the **User** table. Instead we can add a new column with **UserName** to avoid this scenario\n```\nALTER TABLE [dbo].[Order]\n    ADD [UserName] NVARCHAR (50) NULL;\n```\nAlso edit the **Order** table and add the **UserNames** in the table.\nAnd update the query\n```\n\tpublic class OrderDetailsProvider : IOrderDetailsProvider\n    {\n        private readonly string _connectionString;\n        public OrderDetailsProvider(string connectionString)\n        {\n            _connectionString = connectionString;\n        }\n        public OrderDetail[] Get()\n        {\n            using var connection = new SqlConnection(_connectionString);\n\n           return connection.Query\u003cOrderDetail\u003e(@\"SELECT o.UserName AS [USER], od.ProductName AS Name, od.Quantity from [Order] o\n\t\t\t\t\t\t\t\t\t\t\t\t\tJOIN [OrderDetails] od ON o.Id = od.OrderId\"\n            )\n                .ToArray();\n        }\n    }\n```\n---\nRabbitMQ:   \n- What is a Message Broker\n    - You can think of a message broker like a post office. \n    - Its main responsibility is to broker messages between publisher and subscribers\n    - Once a message is received by a message broker from a producer, it routes the message to a subscriber. \n    - Message broker pattern is one of the most useful pattern when it comes to ddecoupling microservices.\n    - **Producer**: An application responsible for sending message\n    - **Consumer**: An application listening for messages.\n    - **Queue**: Storage where messages are stored by the broker.\n- What is RabbitMQ\n    - RabbitMQ is an open source message broker.\n    - It is probably one of the most widely used message broker out there\n    - RabbitMQ is extremely lightweight and very easy to deploy\n    - RabbitMQ supports multiple protocols\n    - RabbitMQ is highly available and scalable\n    - RabbitMQ supports multiple operating systems\n- Protocols Supported:\n    - **AMQP 0-9-1**: a binary messaging protocol specification. This is the core protocol specification implemented in RabbitMQ. All other protocol support in RabbitMQ is through Plugins\n    - **STOMP**: A text based message protocol\n    - **MQTT**: Binary protocol focusing mainly on Publish/Subscribe scenarios\n    - **AMQP 1.0**\n    - **HTTP and WebSocket**\n\n26. Let's create a console application **RabbitMQ.Producer** in the same solution\n27. Now, let't create a docker image for **RabbitMQ** and open up the browser for ***http://localhost:15672/#/** with **guest** username and **guest** password\n```\ndocker images\ndocker run -d --hostname my-rabbit --name ecomm-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3-management\ndocker logs -f e67\n```\n28. Search for the package **RabbitMQ.Client** and install on **RabbitMQ.Producer**\n29. Now add the following codes in **Program.cs**\n```\nvar factory = new ConnectionFactory\n    {\n        Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n    };\nusing var connection = factory.CreateConnection();\nusing var channel = connection.CreateModel();\nchannel.QueueDeclare(\"demo-queue\",\n                      durable: true,\n                      exclusive: false,\n                      autoDelete: false,\n                      arguments: null);\nvar message = new { Name = \"Producer\", Message = \"Hello!\" };\nvar body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\nchannel.BasicPublish(\"\", \"demo-queue\", null, body);\n```\nFor using JsonConvert.SerializeObject, install the package Newtonsoft.json\n30. Now create another console application named **RabbitMQ.Consumer** on the same solution. Also add the package **RabbitMQ.Client**\n31. Now add the following codes into **Program.cs**\n```\nvar factory = new ConnectionFactory\n{\n    Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n};\nusing var connection = factory.CreateConnection();\nusing var channel = connection.CreateModel();\nchannel.QueueDeclare(\"demo-queue\",\n                      durable: true,\n                      exclusive: false,\n                      autoDelete: false,\n                      arguments: null);\n\n\nvar consumer = new EventingBasicConsumer(channel);\nconsumer.Received += (sender, e) =\u003e\n{\n    var body = e.Body.ToArray();\n    var message = Encoding.UTF8.GetString(body);\n    Console.WriteLine($\"{message}\");\n};\n\nchannel.BasicConsume(\"demo-queue\", true, consumer);\nConsole.ReadLine();\n```\n32. Now run the consumer first, it will create the queue in the RabbitMQ server; then run the producer, it will send a message into the queue and you can see it coming in the queue. And finally run the consumer again, you will see the message is received and being print in the console according to the code.\n---\n33. Now go to **RabbitMQ.Producer**, open up **program.cs** file and take the following code from that class\n    ```\n    channel.QueueDeclare(\"demo-queue\",\n                        durable: true,\n                        exclusive: false,\n                        autoDelete: false,\n                        arguments: null);\n    var message = new { Name = \"Producer\", Message = \"Hello!\" };\n    var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\n    channel.BasicPublish(\"\", \"demo-queue\", null, body);\n    ```\nAnd now create a new static class **QueueProducer.cs** and place the code here into a static funciton\n    ```\n    public static void Publish(IModel channel)\n    {\n        channel.QueueDeclare(\"demo-queue\",\n                      durable: true,\n                      exclusive: false,\n                      autoDelete: false,\n                      arguments: null);\n        var message = new { Name = \"Producer\", Message = \"Hello!\" };\n        var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\n        channel.BasicPublish(\"\", \"demo-queue\", null, body);\n    }\n    ```\nNow call that function from **program.cs** file by the following code\n    ```\n    var factory = new ConnectionFactory\n    {\n        Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n    };\n    using var connection = factory.CreateConnection();\n    using var channel = connection.CreateModel();\n    QueueProducer.Publish(channel);\n    ```\n34. To publish multiple messages at the same time, let's modify the **QueueProducer.cs**\n    ```\n    public static class QueueProducer\n    {\n        public static void Publish(IModel channel)\n        {\n            channel.QueueDeclare(\"demo-queue\",\n                        durable: true,\n                        exclusive: false,\n                        autoDelete: false,\n                        arguments: null);\n            \n            var count = 0;\n            while(true)\n            {\n                var message = new { Name = \"Producer\", Message = $\"Hello! Count:{count}\" };\n                var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\n                channel.BasicPublish(\"\", \"demo-queue\", null, body);\n                count++;\n                Thread.Sleep(1000);\n            }\n\n        }\n    }    \n    ```\n35. Let's go to **RabbitMQ.Consumer** and open up the **program.cs** file and take the following code portion\n    ```\n        channel.QueueDeclare(\"demo-queue\",\n                            durable: true,\n                            exclusive: false,\n                            autoDelete: false,\n                            arguments: null);\n\n\n        var consumer = new EventingBasicConsumer(channel);\n        consumer.Received += (sender, e) =\u003e\n        {\n            var body = e.Body.ToArray();\n            var message = Encoding.UTF8.GetString(body);\n            Console.WriteLine($\"{message}\");\n        };\n\n        channel.BasicConsume(\"demo-queue\", true, consumer);\n        Console.ReadLine();\n    ```\n    And create a new class **QueueConsumer.cs** and add the taken code into the following static method\n    ```\n        public static class QueueConsumer\n        {\n            public static void Consume(IModel channel)\n            {\n                channel.QueueDeclare(\"demo-queue\",\n                            durable: true,\n                            exclusive: false,\n                            autoDelete: false,\n                            arguments: null);\n\n\n                var consumer = new EventingBasicConsumer(channel);\n                consumer.Received += (sender, e) =\u003e\n                {\n                    var body = e.Body.ToArray();\n                    var message = Encoding.UTF8.GetString(body);\n                    Console.WriteLine($\"{message}\");\n                };\n\n                channel.BasicConsume(\"demo-queue\", true, consumer);\n                Console.WriteLine(\"Consumer Started\");\n                Console.ReadLine();\n            }\n        }\n    ```\n    And call the method **Consume** from **program.cs** class\n    ```\n    QueueConsumer.Consume(channel);\n    ```\n36. Now let's run the **RabbitMQ.Consumer** from two instances, and run the **RabbitMQ.Producer** then. You will notice that one instance of the consumer is getting the even messges and the other one is getting the odd messges. \nSo, if we have multiple consumers to a single queue, the messages will be evenly distributed across the consumers.\nRabbitMQ gives us the option to scale the service by providing the horizontal distribution. It also ensures 1 consumer gets unique message, 1 message does not go to multiple consumers.\n\nRabbitMQ:   \n- What is an Exchange?\n    - Exchanges are exactly the name suggests\n    - The are exchanges for message\n    - Just like a stock exchange, where people exchanges stocks, a seller sells stocks to a buyer. And exchange acts as a router of the stocks\n    - Similarly, Exchanges in RabbitMQ routes messages from a producer to a single consumer or multiple consumers\n    - An exchange uses **header attributes**, **routing keys** and **binding** to route messages\n    - In RabbitMQ, infact, messages are never published to a queue, they always goes through an Exchange. Event when we send message to a queue it uses default exchange. **[Exchange:(AMQP default)]**\nTypes of Exchange:\n    - **Direct**: Direct exchange uses **routing key** in the header to identify which queue the message should be sent to. Routing key is a **header value** set by the **producer**. And consumers uses the **routing key** to **bind** to the **queue**. The exchange does **exact match** of routing key values.\n    - **Topic**: This is kind of **similar** to Direct. Topic exchange also uses **routing key**, but it does not do an **exact match** on the routing key, instead it does a **pattern match** based on pattern.\n    - **Header**: Header excahnge routes messages based on **header values** and are very similar to Topic exchange\n    - **Fanout**: As the name suggests, Fanout exchange routes messages to all the queues bound to it. It routes all the messages to all the queues, it does not look into the routing keys or anything else.\n\n37. Now, let's explore the **Direct Exchange**. Let's go to **RabbitMQ.Producer** and create a class named **DirectExchangePublisher** and add the following code.\n    ```\n    public static class DirectExchangePublisher\n    {\n        public static void Publish(IModel channel)\n        {\n            channel.ExchangeDeclare(\"demo-direct-exchange\",ExchangeType.Direct);\n            \n            var count = 0;\n            while(true)\n            {\n                var message = new { Name = \"Producer\", Message = $\"Hello! Count:{count}\" };\n                var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\n                channel.BasicPublish(\"demo-direct-exchange\", \"account-init\", null, body);\n                count++;\n                Thread.Sleep(1000);\n            }\n        }\n    }\n    ```\nAlso, modify the **program.cs** and call **DirectExchangePublisher** instead of **QueueProducer**\n    ```\n    //QueueProducer.Publish(channel);\n    DirectExchangePublisher.Publish(channel);\n    ```\n38. Now let's do the same thing for **RabbitMQ.Consumer** and create a new class called **DirectExchangeConsumer.cs** and add the following code.\n    ```\n    public static class DirectExchangeConsumer\n    {\n        public static void Consume(IModel channel)\n        {\n            channel.ExchangeDeclare(\"demo-direct-exchange\", ExchangeType.Direct);\n            channel.QueueDeclare(\"demo-direct-queue\",\n                        durable: true,\n                        exclusive: false,\n                        autoDelete: false,\n                        arguments: null);\n            channel.QueueBind(\"demo-direct-queue\",\"demo-direct-exchange\",\"account-init\");\n\n            var consumer = new EventingBasicConsumer(channel);\n            consumer.Received += (sender, e) =\u003e\n            {\n                var body = e.Body.ToArray();\n                var message = Encoding.UTF8.GetString(body);\n                Console.WriteLine($\"{message}\");\n            };\n\n            channel.BasicConsume(\"demo-direct-queue\", true, consumer);\n            Console.WriteLine(\"Consumer Started\");\n            Console.ReadLine();\n        }\n    }\n    ```\nAlso modify the **program.cs** to call the **DirectExchangeConsumer** method\n\n39. Now let's run the consumer and check out the RabbitMQ server. You will find out your exchange **demo-direct-exchange** and exchange-queue **demo-direct-queue** in the server. And if you check out the exchange bindings, you will find out the exhange is binded to the queue with routing-key **account-init**\nNow run the **RabbitMQ.Producer** and check out messages coming from your defined exhange.\n\n40. Now, we will explore 2 more things, one is **Lifetime of a message** and the other one is **Prefetch count**. These are 2 important concepts for queue.\n**Prefetch Count**: When we have multiple consumers connected to a queue, then **Prefetch count** tell how many messages that perticular consumer can prefetch and process. If prefetch count is 2, if there are 10 messages to the queue, every consumer will take only 2 message and those 2 will be delivered only.\nNow let's implement these 2 concepts and start with implementing the **Lifetime of a message** by modifying the **DirectExchangePublisher.cs** in the **RabbitMQ.Producer**\n```\n    public static class DirectExchangePublisher\n    {\n        public static void Publish(IModel channel)\n        {\n            var ttl = new Dictionary\u003cstring, Object\u003e\n            {\n                { \"x-message-ttl\", 30000 }\n            };\n            channel.ExchangeDeclare(\"demo-direct-exchange\",ExchangeType.Direct, arguments: ttl);\n            \n            var count = 0;\n            while(true)\n            {\n                var message = new { Name = \"Producer\", Message = $\"Hello! Count:{count}\" };\n                var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\n                channel.BasicPublish(\"demo-direct-exchange\", \"account-init\", null, body);\n                count++;\n                Thread.Sleep(1000);\n            }\n        }\n    }\n```\nNow, let's implement the **Prefetch Count** in the **DirectExchangeConsumer.cs** in the **RabbitMQ.Consumer**\n```\npublic static class DirectExchangeConsumer\n{\n    public static void Consume(IModel channel)\n    {\n        channel.ExchangeDeclare(\"demo-direct-exchange\", ExchangeType.Direct);\n        channel.QueueDeclare(\"demo-direct-queue\",\n                      durable: true,\n                      exclusive: false,\n                      autoDelete: false,\n                      arguments: null);\n        channel.QueueBind(\"demo-direct-queue\",\"demo-direct-exchange\",\"account-init\");\n        channel.BasicQos(0,10,false);\n\n        var consumer = new EventingBasicConsumer(channel);\n        consumer.Received += (sender, e) =\u003e\n        {\n            var body = e.Body.ToArray();\n            var message = Encoding.UTF8.GetString(body);\n            Console.WriteLine($\"{message}\");\n        };\n\n        channel.BasicConsume(\"demo-direct-queue\", true, consumer);\n        Console.WriteLine(\"Consumer Started\");\n        Console.ReadLine();\n    }\n}\n```\nNow go to the RabbitMQ dashboard and delete the queue and the exchange. And then run both the consumer and producer, you will see the **Prefetch count** in the queue section and **TTL** in the exchange section.\n41. Create a class **TopicExchangeConsumer.cs** in **RabbitMQ.Consumer**\n```\n    public static void Consume(IModel channel)\n    {\n        channel.ExchangeDeclare(\"demo-topic-exchange\", ExchangeType.Topic);\n        channel.QueueDeclare(\"demo-topic-queue\",\n                      durable: true,\n                      exclusive: false,\n                      autoDelete: false,\n                      arguments: null);\n        channel.QueueBind(\"demo-topic-queue\",\"demo-topic-exchange\",\"account.*\");\n        channel.BasicQos(0,10,false);\n\n        var consumer = new EventingBasicConsumer(channel);\n        consumer.Received += (sender, e) =\u003e\n        {\n            var body = e.Body.ToArray();\n            var message = Encoding.UTF8.GetString(body);\n            Console.WriteLine($\"{message}\");\n        };\n\n        channel.BasicConsume(\"demo-topic-queue\", true, consumer);\n        Console.WriteLine(\"Consumer Started\");\n        Console.ReadLine();\n    }\n```\nNow update the **program.cs** -\n```\n// See https://aka.ms/new-console-template for more information\nusing RabbitMQ.Client;\nusing RabbitMQ.Consumer;\n\nConsole.WriteLine(\"Hello, World!\");\n\n\n\nvar factory = new ConnectionFactory\n{\n    Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n};\nusing var connection = factory.CreateConnection();\nusing var channel = connection.CreateModel();\n// QueueConsumer.Consume(channel);\n//DirectExchangeConsumer.Consume(channel);\nTopicExchangeConsumer.Consume(channel);\n```\n42. Create a new class **TopicExchangeProducer** in **RabbitMQ.Producer**\n```\n    public static void Publish(IModel channel)\n    {\n        var ttl = new Dictionary\u003cstring, Object\u003e\n        {\n            { \"x-message-ttl\", 30000 }\n        };\n        channel.ExchangeDeclare(\"demo-topic-exchange\",ExchangeType.Topic, arguments: ttl);\n        \n        var count = 0;\n        while(true)\n        {\n            var message = new { Name = \"Producer\", Message = $\"Hello! Count:{count}\" };\n            var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\n            channel.BasicPublish(\"demo-topic-exchange\", \"account.update\", null, body);\n            count++;\n            Thread.Sleep(1000);\n        }\n    }\n```\nNow update the **program.cs** -\n```\n// See https://aka.ms/new-console-template for more information\nusing RabbitMQ.Client;\nusing RabbitMQ.Producer;\n\nConsole.WriteLine(\"Hello, World!\");\n\nvar factory = new ConnectionFactory\n    {\n        Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n    };\nusing var connection = factory.CreateConnection();\nusing var channel = connection.CreateModel();\n//QueueProducer.Publish(channel);\n//DirectExchangePublisher.Publish(channel);\nTopicExchangeProducer.Publish(channel);\n```   \n\n43. Now run both the **producer** and **consumer** and you will see **consumer** is going to receive the messages. Now update the **routing-key** into **account.update** in  **producer** and check if messages are consumed in the consumer or not. Also try with **user.update** as **routing-key** in the **producer** and check if the messages are consumed or not.\n\n44. Create a new class **HeaderExchangeConsumer** in **RabbitMQ.Consumer** \n```\n    public static void Consume(IModel channel)\n    {\n        channel.ExchangeDeclare(\"demo-header-exchange\", ExchangeType.Headers);\n        channel.QueueDeclare(\"demo-header-queue\",\n                      durable: true,\n                      exclusive: false,\n                      autoDelete: false,\n                      arguments: null);\n\n        var header = new Dictionary\u003cstring, object\u003e {{ \"account\", \"new\" }};\n\n        channel.QueueBind(\"demo-header-queue\",\"demo-header-exchange\",string.Empty,header);\n        channel.BasicQos(0,10,false);\n\n        var consumer = new EventingBasicConsumer(channel);\n        consumer.Received += (sender, e) =\u003e\n        {\n            var body = e.Body.ToArray();\n            var message = Encoding.UTF8.GetString(body);\n            Console.WriteLine($\"{message}\");\n        };\n\n        channel.BasicConsume(\"demo-header-queue\", true, consumer);\n        Console.WriteLine(\"Header Consumer Started\");\n        Console.ReadLine();\n    }\n```\nAlso update the **program.cs** file\n```\n// See https://aka.ms/new-console-template for more information\nusing RabbitMQ.Client;\nusing RabbitMQ.Consumer;\n\nConsole.WriteLine(\"Hello, World!\");\n\n\n\nvar factory = new ConnectionFactory\n{\n    Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n};\nusing var connection = factory.CreateConnection();\nusing var channel = connection.CreateModel();\n// QueueConsumer.Consume(channel);\n//DirectExchangeConsumer.Consume(channel);\n//TopicExchangeConsumer.Consume(channel);\nHeaderExchangeConsumer.Consume(channel);\n```\n45. Create a new class **HeaderExchangeProducer** in **RabbitMQ.Producer** \n```\n    public static void Publish(IModel channel)\n    {\n        var ttl = new Dictionary\u003cstring, Object\u003e\n        {\n            { \"x-message-ttl\", 30000 }\n        };\n        channel.ExchangeDeclare(\"demo-header-exchange\",ExchangeType.Headers, arguments: ttl);\n        \n        var count = 0;\n        while(true)\n        {\n            var message = new { Name = \"Producer\", Message = $\"Hello! Count:{count}\" };\n            var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\n            var properties = channel.CreateBasicProperties();\n            properties.Headers = new Dictionary\u003cstring, Object\u003e { { \"account\", \"new\" } };\n\n            channel.BasicPublish(\"demo-header-exchange\", string.Empty, properties, body);\n            count++;\n            Thread.Sleep(1000);\n        }\n    }\n```\nAlso update the **program.cs** file\n```\n// See https://aka.ms/new-console-template for more information\nusing RabbitMQ.Client;\nusing RabbitMQ.Producer;\n\nConsole.WriteLine(\"Hello, World!\");\n\nvar factory = new ConnectionFactory\n    {\n        Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n    };\nusing var connection = factory.CreateConnection();\nusing var channel = connection.CreateModel();\n//QueueProducer.Publish(channel);\n//DirectExchangePublisher.Publish(channel);\n//TopicExchangeProducer.Publish(channel);\nHeaderExchangeProducer.Publish(channel);\n``` \n46. Now run both the **producer** and **consumer** and check if **consumer** can consume the messages. But if you change the **header** value in **producer** and run the applications, you will see, **exchange** will get the messages but the **consumer** will not get any messages\n47. Create a new class **FanoutExchangeConsumer** in **RabbitMQ.Consumer** \n```\n    public static void Consume(IModel channel)\n    {\n        channel.ExchangeDeclare(\"demo-fanout-exchange\", ExchangeType.Fanout);\n        channel.QueueDeclare(\"demo-fanout-queue\",\n                      durable: true,\n                      exclusive: false,\n                      autoDelete: false,\n                      arguments: null);\n\n\n        channel.QueueBind(\"demo-fanout-queue\",\"demo-fanout-exchange\",string.Empty);\n        channel.BasicQos(0,10,false);\n\n        var consumer = new EventingBasicConsumer(channel);\n        consumer.Received += (sender, e) =\u003e\n        {\n            var body = e.Body.ToArray();\n            var message = Encoding.UTF8.GetString(body);\n            Console.WriteLine($\"{message}\");\n        };\n\n        channel.BasicConsume(\"demo-fanout-queue\", true, consumer);\n        Console.WriteLine(\"Fanout Consumer Started\");\n        Console.ReadLine();\n    }\n```\nAlso update the **program.cs** -\n```\n// See https://aka.ms/new-console-template for more information\nusing RabbitMQ.Client;\nusing RabbitMQ.Consumer;\n\nConsole.WriteLine(\"Hello, World!\");\n\n\n\nvar factory = new ConnectionFactory\n{\n    Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n};\nusing var connection = factory.CreateConnection();\nusing var channel = connection.CreateModel();\n// QueueConsumer.Consume(channel);\n//DirectExchangeConsumer.Consume(channel);\n//TopicExchangeConsumer.Consume(channel);\n//HeaderExchangeConsumer.Consume(channel);\nFanoutExchangeConsumer.Consume(channel);\n```\n48. Create a new class **FanoutExhangeProducer** in **RabbitMQ.Producer**\n```\n    public static void Publish(IModel channel)\n    {\n        var ttl = new Dictionary\u003cstring, Object\u003e\n        {\n            { \"x-message-ttl\", 30000 }\n        };\n        channel.ExchangeDeclare(\"demo-fanout-exchange\",ExchangeType.Fanout, arguments: ttl);\n        \n        var count = 0;\n        while(true)\n        {\n            var message = new { Name = \"Producer\", Message = $\"Hello! Count:{count}\" };\n            var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));\n\n            var properties = channel.CreateBasicProperties();\n            properties.Headers = new Dictionary\u003cstring, Object\u003e { { \"account\", \"new\" } };/* not needed but kept it to prove that fanout will go to every consumer*/\n\n            channel.BasicPublish(\"demo-fanout-exchange\", string.Empty, properties, body);\n            count++;\n            Thread.Sleep(1000);\n        }\n    }\n```\nAlso update the **program.cs** -\n```\n// See https://aka.ms/new-console-template for more information\nusing RabbitMQ.Client;\nusing RabbitMQ.Producer;\n\nConsole.WriteLine(\"Hello, World!\");\n\nvar factory = new ConnectionFactory\n    {\n        Uri = new Uri(\"amqp://guest:guest@localhost:5672\")\n    };\nusing var connection = factory.CreateConnection();\nusing var channel = connection.CreateModel();\n//QueueProducer.Publish(channel);\n//DirectExchangePublisher.Publish(channel);\n//TopicExchangeProducer.Publish(channel);\n//HeaderExchangeProducer.Publish(channel);\nFanoutExchangeProducer.Publish(channel);\n```\n49. Now run multiple instances of **consumer** and run 1 instance of **producer**, will get to observe that every **consumer** will get the messages. If you observe that although the **producer** has **header** properties, **consumers** are getting all the messages. Even if you add **routing-key** in  **producer**, **consumers** will still receive the messages\n50. \n---\nReference:   \n- https://www.youtube.com/watch?v=atJkRk_MwdU\u0026list=PLXCqSX1D2fd_6bna8uP4-p3Y8wZxyB75G\u0026index=1\u0026ab_channel=DotNetCoreCentral  \n- https://www.youtube.com/watch?v=3AKqtggkaIA\u0026list=PLXCqSX1D2fd_6bna8uP4-p3Y8wZxyB75G\u0026index=2\u0026ab_channel=DotNetCoreCentral\n- https://www.youtube.com/watch?v=w84uFSwulBI\u0026list=PLXCqSX1D2fd_6bna8uP4-p3Y8wZxyB75G\u0026index=3\u0026ab_channel=DotNetCoreCentral\n- https://www.youtube.com/watch?v=Cm2psU-zN90\u0026list=PLXCqSX1D2fd_6bna8uP4-p3Y8wZxyB75G\u0026index=4\u0026ab_channel=DotNetCoreCentral\n- https://www.youtube.com/watch?v=EtTPtnn6uKE\u0026list=PLXCqSX1D2fd_6bna8uP4-p3Y8wZxyB75G\u0026index=5\u0026ab_channel=DotNetCoreCentral","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimtiajahammad%2Fecomm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimtiajahammad%2Fecomm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimtiajahammad%2Fecomm/lists"}