├── Docs ├── cb.JPG ├── cq.JPG ├── ec.JPG ├── ed.JPG ├── cap.JPG └── highlevel.JPG ├── Src ├── InventoryMicroservice │ ├── InventoryCore │ │ ├── Class1.cs │ │ └── InventoryCore.csproj │ ├── InventoryDomainService │ │ ├── Class1.cs │ │ └── InventoryDomainService.csproj │ ├── InventoryQueryHandler │ │ ├── Class1.cs │ │ └── InventoryQueryHandler.csproj │ ├── InventoryInfrastructure │ │ ├── Class1.cs │ │ └── InventoryInfrastructure.csproj │ ├── Inventory.Event │ │ └── Inventory.Event │ │ │ ├── PurchaseEvent.cs │ │ │ └── Inventory.Event.csproj │ ├── InventoryQuery │ │ ├── StoreQuery.cs │ │ └── Inventory.Query.csproj │ ├── InventoryDto │ │ ├── Inventory.DTO.csproj │ │ ├── StoreQueryResultDTO.cs │ │ └── StoreDTO.cs │ ├── InventoryWebApi │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Constants.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Program.cs │ │ ├── Controllers │ │ │ ├── StoreQueryController.cs │ │ │ └── StoreCommandController.cs │ │ ├── Inventory.Api.csproj │ │ └── Startup.cs │ ├── Inventory.Api.Grpc │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Protos │ │ │ └── InventoryService.proto │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Services │ │ │ └── GreeterService.cs │ │ ├── Inventory.Api.Grpc.csproj │ │ ├── Dockerfile │ │ ├── Program.cs │ │ └── Startup.cs │ ├── Inventory.Api.Tests │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Inventory.Api.Tests.csproj │ │ ├── TestClientProvider.cs │ │ ├── StoredCommandControllerTest.cs │ │ └── StoreQueryControllerTest.cs │ ├── InventoryCommand │ │ ├── CreateStoreCommand.cs │ │ └── Inventory.Command.csproj │ ├── InventoryDomain │ │ ├── Store.cs │ │ ├── Inventory.Domain.csproj │ │ └── StoreItem.cs │ ├── Inventory.Api.Grpc.Tests │ │ ├── Protos │ │ │ └── InventoryService.proto │ │ ├── InventoryServiceTest.cs │ │ └── Inventory.Api.Grpc.Tests.csproj │ ├── InventoryRepository │ │ ├── IStoreRepository.cs │ │ ├── IStoreItemRepository.cs │ │ ├── Inventory.Repository.csproj │ │ ├── StoreRepository.cs │ │ └── StoreItemRepository.cs │ ├── Inventory.EventHandler │ │ └── Inventory.EventHandler │ │ │ ├── Inventory.EventHandler.csproj │ │ │ ├── ProductSoldEventHandler.cs │ │ │ └── ProductPurchasedEventHandler.cs │ ├── InventoryCommandHandler │ │ ├── Inventory.CommandHandler.csproj │ │ └── CreateStoreCommandHandler.cs │ └── Inventory.QueryHandler │ │ ├── Inventory.QueryHandler.csproj │ │ └── StoreQueryHandler.cs ├── SalesMicroservice │ ├── SalesDomainService │ │ ├── Class1.cs │ │ └── SalesDomainService.csproj │ ├── SalesInfrastructure │ │ ├── Class1.cs │ │ └── SalesInfrastructure.csproj │ ├── SalesDto │ │ ├── Sales.DTO.csproj │ │ ├── ProductSalesDTO.cs │ │ └── SalesDTO.cs │ ├── SalesCore │ │ ├── Sales.Core.csproj │ │ └── Constants.cs │ ├── SalesWebApi │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Program.cs │ │ ├── Dockerfile │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Controllers │ │ │ ├── SalesQueryController.cs │ │ │ └── SalesCommandController.cs │ │ ├── Sales.Api.csproj │ │ └── Startup.cs │ ├── SalesQuery │ │ ├── Sales.Query.csproj │ │ └── SalesQuery.cs │ ├── SalesCommand │ │ ├── Sales.Command.csproj │ │ └── SalesCommand.cs │ ├── SalesDomain │ │ ├── Sales.Domain.csproj │ │ ├── Sales.cs │ │ └── SalesLineItem.cs │ ├── SalesRepository │ │ ├── ISalesRepository.cs │ │ ├── Sales.Repository.csproj │ │ └── SalesRepository.cs │ ├── SalesQueryHandler │ │ ├── Sales.QueryHandler.csproj │ │ └── SalesQueryHandler.cs │ ├── SalesCommandHandler │ │ ├── Sales.CommandHandler.csproj │ │ └── SalesCommandHandler.cs │ └── Sales.Repository.Tests │ │ ├── Sales.Repository.Tests.csproj │ │ └── SalesRepositoryTest.cs ├── PurchaseMicroservice │ ├── PurchaseDto │ │ ├── Purchase.DTO.csproj │ │ ├── PurchaseItemDTO.cs │ │ ├── ProductPurchasedDTO.cs │ │ ├── PurchaseQueryDTO.cs │ │ └── PurchaseDTO.cs │ ├── PurchaseDomain │ │ ├── Models │ │ │ ├── User.cs │ │ │ ├── Product.cs │ │ │ ├── Purchase.cs │ │ │ └── ProductLineItem.cs │ │ └── Purchase.Domain.csproj │ ├── PurchaseWebApi │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Program.cs │ │ ├── Purchase.Api.csproj │ │ ├── Controllers │ │ │ ├── PurchaseQueryController.cs │ │ │ └── PurchaseCommandController.cs │ │ └── Startup.cs │ ├── PurchaseInfrastructure │ │ ├── IServiceBus.cs │ │ ├── Purchase.Infrastructure.csproj │ │ └── RabbitMqBus.cs │ ├── PurchaseCore │ │ ├── Purchase.Core.csproj │ │ ├── BusinessException.cs │ │ └── Constants.cs │ ├── PurchaseQuery │ │ ├── Purchase.Query.csproj │ │ └── ProductPurchasedQuery.cs │ ├── PurchaseCommandHandler │ │ ├── Protos │ │ │ └── InventoryService.proto │ │ ├── Purchase.CommandHandler.csproj │ │ └── PurchaseCommandHandler.cs │ ├── PurchaseCommand │ │ ├── LineItemCommand.cs │ │ ├── Purchase.Command.csproj │ │ └── PurchaseCommand.cs │ ├── PurchaseRepository │ │ ├── IPurchaseRepostiory.cs │ │ ├── Purchase.Repository.csproj │ │ └── PurchaseRepository.cs │ └── Purchase.QueryHandler │ │ ├── Purchase.QueryHandler.csproj │ │ └── ProductPurchaseQueryHandler.cs ├── CommonAll │ ├── Common.Core │ │ ├── IEvent.cs │ │ ├── IQuery.cs │ │ ├── ICommand.cs │ │ ├── IAggregate.cs │ │ ├── Aggregate.cs │ │ ├── IEmailService.cs │ │ ├── IQueryHandler.cs │ │ ├── IEventHandler.cs │ │ ├── ICommandBus.cs │ │ ├── ICommandHandler.cs │ │ ├── IRepository.cs │ │ ├── ISerializer.cs │ │ ├── Common.Core.csproj │ │ ├── QueryResult.cs │ │ ├── IEventBus.cs │ │ ├── IQueryBus.cs │ │ ├── CommandResult.cs │ │ ├── EmailSettings.cs │ │ ├── EmailParams.cs │ │ ├── Entity.cs │ │ ├── JsonSerializer.cs │ │ ├── IMongoService.cs │ │ └── Events │ │ │ ├── ProductPurchasedEvent.cs │ │ │ └── ProductSoldEvent.cs │ ├── Common.Core.Tests │ │ ├── UnitTest1.cs │ │ └── Common.Core.Tests.csproj │ ├── Common.Infrastructure │ │ ├── MessageBrokerSettings.cs │ │ ├── Common.Infrastructure.csproj │ │ ├── QueryBus.cs │ │ ├── CommandBus.cs │ │ ├── EmailService.cs │ │ ├── MongoService.cs │ │ └── EventBus.cs │ └── Common.Infrastructure.Tests │ │ ├── Common.Infrastructure.Tests.csproj │ │ └── RabbitMqServiceBusTest.cs ├── .dockerignore └── TheMicroservices.sln ├── LICENSE ├── .gitignore └── README.md /Docs/cb.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/habibsql/TheMicroServices/HEAD/Docs/cb.JPG -------------------------------------------------------------------------------- /Docs/cq.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/habibsql/TheMicroServices/HEAD/Docs/cq.JPG -------------------------------------------------------------------------------- /Docs/ec.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/habibsql/TheMicroServices/HEAD/Docs/ec.JPG -------------------------------------------------------------------------------- /Docs/ed.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/habibsql/TheMicroServices/HEAD/Docs/ed.JPG -------------------------------------------------------------------------------- /Docs/cap.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/habibsql/TheMicroServices/HEAD/Docs/cap.JPG -------------------------------------------------------------------------------- /Docs/highlevel.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/habibsql/TheMicroServices/HEAD/Docs/highlevel.JPG -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryCore/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InventoryCore 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesDomainService/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SalesDomainService 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesInfrastructure/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SalesInfrastructure 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryDomainService/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InventoryDomainService 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryQueryHandler/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InventoryQueryHandler 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryInfrastructure/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InventoryInfrastructure 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Event/Inventory.Event/PurchaseEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Inventory.Event 4 | { 5 | public class PurchaseEvent 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesDto/Sales.DTO.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesCore/Sales.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryQuery/StoreQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Query 2 | { 3 | using Common.Core; 4 | using System; 5 | 6 | public class StoreQuery : IQuery 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDto/Purchase.DTO.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryCore/InventoryCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryDto/Inventory.DTO.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public interface IEvent 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public interface IQuery 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesDomainService/SalesDomainService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/ICommand.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public interface ICommand 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesInfrastructure/SalesInfrastructure.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IAggregate.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public interface IAggregate 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryQueryHandler/InventoryQueryHandler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Event/Inventory.Event/Inventory.Event.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryDomainService/InventoryDomainService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryInfrastructure/InventoryInfrastructure.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDomain/Models/User.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Domain.Model 2 | { 3 | using Common.Core; 4 | 5 | public class User : Aggregate 6 | { 7 | public string Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryDto/StoreQueryResultDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Inventory.DTO 6 | { 7 | class StoreQueryResultDTO 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/Aggregate.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public abstract class Aggregate : Entity, IAggregate 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseWebApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace Common.Core.Tests 5 | { 6 | public class UnitTest1 7 | { 8 | [Fact] 9 | public void Test1() 10 | { 11 | 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Grpc": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Tests/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Inventory.Api.Tests": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesCore/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Core 2 | { 3 | using System; 4 | 5 | public class Constants 6 | { 7 | public class MessageQueue 8 | { 9 | public const string SalesQueue = "product-sales"; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryCommand/CreateStoreCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Command 2 | { 3 | using Common.Core; 4 | 5 | public class CreateStoreCommand : ICommand 6 | { 7 | public string StoreId { get; set; } 8 | 9 | public string ManagerName { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseInfrastructure/IServiceBus.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Infrastructure 2 | { 3 | using Common.All; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | public interface IServiceBus 8 | { 9 | Task Publish(string message, string queue); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryDomain/Store.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Domain 2 | { 3 | using Common.Core; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | public class Store : Aggregate 9 | { 10 | public string Manager { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IEmailService.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | public interface IEmailService 9 | { 10 | Task SendEmail(EmailParams emailParams); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IQueryHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System.Threading.Tasks; 4 | 5 | public interface IQueryHandler 6 | where TQuery : IQuery 7 | where TResult : QueryResult 8 | { 9 | public Task Handle(TQuery query); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCore/Purchase.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryDto/StoreDTO.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.DTO 2 | { 3 | using System; 4 | 5 | public class StoreDTO 6 | { 7 | public string StoreId { get; set; } 8 | 9 | public string ManagerName { get; set; } 10 | 11 | public DateTime CreatedDate { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesQuery/Sales.Query.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDomain/Models/Product.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Domain.Model 2 | { 3 | using System; 4 | using Common.Core; 5 | 6 | /// 7 | /// Product Entity 8 | /// 9 | public class Product : Aggregate 10 | { 11 | public string ProductName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesCommand/Sales.Command.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesDomain/Sales.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseQuery/Purchase.Query.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryCommand/Inventory.Command.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryDomain/Inventory.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using Common.Core.Events; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | public interface IEventHandler where T : IEvent 10 | { 11 | Task Handle(T @event); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesQuery/SalesQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Query 2 | { 3 | using Common.Core; 4 | using System; 5 | 6 | public class SalesQuery : IQuery 7 | { 8 | public string ProductId { get; set; } 9 | 10 | public DateTime DateFrom { get; set; } 11 | 12 | public DateTime DateTo { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "ConnectionStrings": { 11 | "Default": "mongodb://localhost:27017/SalesDB" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/ICommandBus.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System.Threading.Tasks; 4 | 5 | public interface ICommandBus 6 | { 7 | Task Send(TCommand command) where TCommand : ICommand 8 | where TResult : CommandResult; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/ICommandHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | public interface ICommandHandler 9 | { 10 | public Task Handle(ICommand command); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | public interface IRepository where T: IAggregate 9 | { 10 | Task Save(T entity); 11 | 12 | Task GetById(string id); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryDomain/StoreItem.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Domain 2 | { 3 | using Common.Core; 4 | using System; 5 | 6 | public class StoreItem : Aggregate 7 | { 8 | public string ItemName { get; set; } 9 | 10 | public Store Store { get; set; } 11 | 12 | public long BalanceQuantity { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "ConnectionStrings": { 10 | "Default": "mongodb://localhost:27017/InvetoryDB" 11 | }, 12 | "AllowedHosts": "*" 13 | } 14 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/ISerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | public interface ISerializer 9 | { 10 | Task Serialize(T t); 11 | 12 | Task Decerialize(string data); 13 | 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "Kestrel": { 11 | "EndpointDefaults": { 12 | "Protocols": "Http2" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/Common.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/QueryResult.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public class QueryResult 8 | { 9 | public string Error { get; set; } 10 | public object Result { get; set; } 11 | 12 | public bool Succeed => string.IsNullOrEmpty(Error); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesDto/ProductSalesDTO.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.DTO 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public class ProductSalesDTO 8 | { 9 | public string ProductId { get; set; } 10 | 11 | public string ProductName { get; set; } 12 | 13 | public long SalesQuantity { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IEventBus.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using Common.Core.Events; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | public interface IEventBus 9 | { 10 | Task Publish(string queue, T @event) where T : IEvent; 11 | 12 | Task Subscribe(string queue) where T : IEvent; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDto/PurchaseItemDTO.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.DTO 2 | { 3 | public class PurchaseItemDTO 4 | { 5 | public string ProductId { get; set; } 6 | 7 | public string ProductName { get; set; } 8 | 9 | public long UnitPrice { get; set; } 10 | 11 | public string UnitName { get; set; } 12 | 13 | public long Quantity { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/Protos/InventoryService.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option csharp_namespace = "Inventory.Api.Grpc"; 4 | 5 | package greet; 6 | 7 | service InventoryServiceProvider { 8 | rpc CountTotalItems (ServiceRequest) returns (ServiceReplay); 9 | } 10 | 11 | message ServiceRequest { 12 | string storeId = 1; 13 | } 14 | 15 | message ServiceReplay { 16 | string itemCount = 1; 17 | } 18 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc.Tests/Protos/InventoryService.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option csharp_namespace = "Inventory.Api.Grpc"; 4 | 5 | package greet; 6 | 7 | service InventoryServiceProvider { 8 | rpc CountTotalItems (ServiceRequest) returns (ServiceReplay); 9 | } 10 | 11 | message ServiceRequest { 12 | string storeId = 1; 13 | } 14 | 15 | message ServiceReplay { 16 | string itemCount = 1; 17 | } 18 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCommandHandler/Protos/InventoryService.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option csharp_namespace = "Inventory.Api.Grpc"; 4 | 5 | package greet; 6 | 7 | service InventoryServiceProvider { 8 | rpc CountTotalItems (ServiceRequest) returns (ServiceReplay); 9 | } 10 | 11 | message ServiceRequest { 12 | string storeId = 1; 13 | } 14 | 15 | message ServiceReplay { 16 | string itemCount = 1; 17 | } 18 | -------------------------------------------------------------------------------- /Src/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryRepository/IStoreRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Repository 2 | { 3 | using Common.Core; 4 | using Inventory.Domain; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | public interface IStoreRepository : IRepository 11 | { 12 | public Task> GetAll(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IQueryBus.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | public interface IQueryBus 9 | { 10 | Task Send(TQuery query) where TQuery : IQuery 11 | where TResult : QueryResult; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure/MessageBrokerSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Common.Infrastructure 6 | { 7 | public class MessageBrokerSettings 8 | { 9 | public string Host { get; set; } 10 | public int Port { get; set; } 11 | 12 | public string UserId { get; set; } 13 | 14 | public string Password { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCore/BusinessException.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public class BusinessException : Exception 8 | { 9 | public BusinessException():base() 10 | { 11 | } 12 | public BusinessException(string message) : base(message) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryQuery/Inventory.Query.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDto/ProductPurchasedDTO.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.DTO 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public class ProductPurchasedDTO 8 | { 9 | public DateTime Date { get; set; } 10 | 11 | public string ProductName { get; set; } 12 | 13 | public long PurchasedQuantity { get; set; } 14 | 15 | public long PurchasedAmount { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | public class Constants 9 | { 10 | public class MessageQueue 11 | { 12 | public const string PurchaseQueue = "product-purchased"; 13 | public const string SalesQueue = "product-sales"; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCommand/LineItemCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Command 2 | { 3 | using Common.Core; 4 | 5 | public class LineItemCommand : ICommand 6 | { 7 | public string ProductId { get; set; } 8 | 9 | public string ProductName { get; set; } 10 | 11 | public string PurchaseUnitName { get; set; } 12 | 13 | public long PurchaseUnitPrice { get; set; } 14 | 15 | public long PurchaseQuantity { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Inventory.Api.Grpc": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | }, 8 | "applicationUrl": "https://localhost:7001" 9 | }, 10 | "Docker": { 11 | "commandName": "Docker", 12 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 13 | "publishAllPorts": true 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesRepository/ISalesRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Repository 2 | { 3 | using Common.Core; 4 | using Sales.DTO; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Sales = Domain.Sales; 10 | 11 | public interface ISalesRepository : IRepository 12 | { 13 | Task FindSoldProductInfo(string productId, DateTime dateFrom, DateTime dateTo); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryRepository/IStoreItemRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Repository 2 | { 3 | using Common.Core; 4 | using Inventory.Domain; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | public interface IStoreItemRepository : IRepository 9 | { 10 | Task SaveItems(IEnumerable entities); 11 | 12 | Task UpdateItem(StoreItem entity); 13 | 14 | Task RemoveItem(string id); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesDomain/Sales.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Domain 2 | { 3 | using Common.Core; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | public class Sales : Aggregate 9 | { 10 | public DateTime SalesDate { get; set; } 11 | 12 | public IList SalesLineItems { get; set; } 13 | 14 | public Sales() 15 | { 16 | this.SalesLineItems = new List(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseWebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | 11 | "EmailSettings": { 12 | "Host": "127.0.0.1", 13 | "Port": 27, 14 | "UserId": "a@gmail.com", 15 | "Password": "****" 16 | }, 17 | "ConnectionStrings": { 18 | "Default": "mongodb://localhost:27017/PurchaseDB" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/CommandResult.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | 5 | public class CommandResult 6 | { 7 | public string Error { get; set; } 8 | 9 | public CommandResult() 10 | { 11 | } 12 | 13 | public CommandResult(string errorData) 14 | { 15 | this.Error = errorData; 16 | } 17 | 18 | public bool Succeed 19 | { 20 | get { return String.IsNullOrEmpty(Error); } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCommand/Purchase.Command.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCore/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Purchase.Core 6 | { 7 | public class Constants 8 | { 9 | public class MessageQueues 10 | { 11 | public const string PurchasedQueue = "product-purchased"; 12 | } 13 | 14 | public class EmailAddressess 15 | { 16 | public const string AdminPurchaseEmailAddress = "admin-purchase@gmail.com"; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/EmailSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public class EmailSettings 8 | { 9 | public string Host { get; set; } 10 | 11 | public int Port { get; set; } 12 | 13 | public string UserId { get; set; } 14 | 15 | public string Password { get; set; } 16 | 17 | public bool Ssl { get; set; } 18 | 19 | public string FromAddress { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseInfrastructure/Purchase.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryRepository/Inventory.Repository.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCommand/PurchaseCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Command 2 | { 3 | using Common.Core; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | public class PurchaseCommand : ICommand 8 | { 9 | public string PurchaseId { get; set; } 10 | 11 | public DateTime PurchaseDate { get; set; } 12 | 13 | public string UserId { get; set; } 14 | 15 | public string UserName { get; set; } 16 | 17 | public IList LineItems { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseRepository/IPurchaseRepostiory.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Repository 2 | { 3 | using Common.Core; 4 | using Purchase.Domain.Model; 5 | using Purchase.DTO; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | public interface IPurchaseRepostiory : IRepository 11 | { 12 | public Task> SearchPurchases(DateTime from, DateTime to, int pageNumber, int pageSize, string sort, int sortDirection); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDomain/Purchase.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure/Common.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesRepository/Sales.Repository.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/EmailParams.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public class EmailParams 8 | { 9 | public List ToList { get; set; } 10 | 11 | public List CcList { get; set; } 12 | 13 | public string Subject { get; set; } 14 | 15 | public string Body { get; set; } 16 | 17 | public EmailParams() 18 | { 19 | ToList = new List(); 20 | CcList = new List(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDomain/Models/Purchase.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Domain.Model 2 | { 3 | using Common.Core; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | /// 8 | /// Product Purchase Entity 9 | /// 10 | public class Purchase : Aggregate 11 | { 12 | public DateTime PurchaseDate { get; set; } 13 | 14 | public User User { get; set; } 15 | 16 | public IList LineItems { get; set; } 17 | 18 | public Purchase() 19 | { 20 | LineItems = new List(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseRepository/Purchase.Repository.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDto/PurchaseQueryDTO.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.DTO 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public class PurchaseQueryDTO 8 | { 9 | public DateTime DateFrom { get; set; } 10 | 11 | public DateTime DateTo { get; set; } 12 | 13 | public int PageNumber { get; set; } 14 | 15 | public int PageSize { get; set; } 16 | 17 | public string SortField { get; set; } 18 | 19 | /// 20 | /// 0 > Asending 1 > Decending 21 | /// 22 | public int SortDirection { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.EventHandler/Inventory.EventHandler/Inventory.EventHandler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesQueryHandler/Sales.QueryHandler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesDomain/SalesLineItem.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Domain 2 | { 3 | using Common.Core; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | public class SalesLineItem 9 | { 10 | public string ProductId { get; set; } 11 | 12 | public string ProductName { get; set; } 13 | 14 | public string UnitName { get; set; } 15 | 16 | public long UnitSalePrice { get; set; } 17 | 18 | public long SalesQuantity { get; set; } 19 | 20 | public long SalesTotalPrice 21 | { 22 | get { return UnitSalePrice * SalesQuantity; } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core.Tests/Common.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/Services/GreeterService.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api.Grpc 2 | { 3 | using global::Grpc.Core; 4 | using Microsoft.Extensions.Logging; 5 | using System.Threading.Tasks; 6 | 7 | public class InventoryService : InventoryServiceProvider.InventoryServiceProviderBase 8 | { 9 | 10 | public InventoryService() 11 | { 12 | } 13 | 14 | public override Task CountTotalItems(ServiceRequest request, ServerCallContext context) 15 | { 16 | // Need to fetch from db. Now it is hardcode value 17 | 18 | return Task.FromResult(new ServiceReplay { ItemCount = "999" }); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryCommandHandler/Inventory.CommandHandler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using MongoDB.Bson.Serialization.Attributes; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | public abstract class Entity 9 | { 10 | [BsonElement("_id")] 11 | public string Id { get; set; } 12 | 13 | public DateTime CreatedOn { get; set; } 14 | 15 | public DateTime UpdatedOn { get; set; } 16 | 17 | public string CreatedBy { get; set; } 18 | 19 | public string UpdatedBy { get; set; } 20 | 21 | public Entity() 22 | { 23 | CreatedOn = DateTime.UtcNow; 24 | UpdatedOn = DateTime.UtcNow; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDomain/Models/ProductLineItem.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Domain.Model 2 | { 3 | using Common.Core; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | /// 9 | /// Product Purchase Line Item Entity 10 | /// 11 | public class ProductLineItem : Entity 12 | { 13 | public Product Product { get; set; } 14 | 15 | public long PurchaseUnitPrice { get; set; } 16 | 17 | public long PurchaseQuantity { get; set; } 18 | 19 | public long PurchaseTotalPrice 20 | { 21 | get 22 | { 23 | return PurchaseQuantity * PurchaseUnitPrice; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/Inventory.Api.Grpc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 98f4083c-b443-499d-8fc3-ac5c3cda9802 6 | Linux 7 | ..\.. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/JsonSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Text.Json; 7 | using System.Threading.Tasks; 8 | 9 | public class JsonSerializer : ISerializer 10 | { 11 | public virtual Task Serialize(T obj) 12 | { 13 | string jsonData = System.Text.Json.JsonSerializer.Serialize(obj); 14 | 15 | return Task.FromResult(jsonData); 16 | } 17 | 18 | public virtual Task Decerialize(string jsonData) 19 | { 20 | T obj = System.Text.Json.JsonSerializer.Deserialize(jsonData); 21 | 22 | return Task.FromResult(obj); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseDto/PurchaseDTO.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.DTO 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public class PurchaseDTO 7 | { 8 | public DateTime PurchaseDate { get; set; } 9 | 10 | public IList PurchaseItems { get; set; } 11 | 12 | public PurchaseDTO() 13 | { 14 | PurchaseItems = new List(); 15 | } 16 | 17 | public long GetTotalPrice() 18 | { 19 | long total = 0; 20 | 21 | foreach (PurchaseItemDTO dto in PurchaseItems) 22 | { 23 | total += dto.UnitPrice * dto.Quantity; 24 | } 25 | 26 | return total; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesDto/SalesDTO.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.DTO 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public class SalesDTO 7 | { 8 | public DateTime SalesDate { get; set; } 9 | 10 | public IList SalesProducts { get; set; } 11 | 12 | public SalesDTO() 13 | { 14 | this.SalesProducts = new List(); 15 | } 16 | } 17 | 18 | public class SalesProductDTO 19 | { 20 | public string ProductId { get; set; } 21 | 22 | public string ProductName { get; set; } 23 | 24 | public string UnitName { get; set; } 25 | 26 | public long UnitPrice { get; set; } 27 | 28 | public long Quantity { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.QueryHandler/Inventory.QueryHandler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesCommandHandler/Sales.CommandHandler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:53158", 7 | "sslPort": 44318 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "InventoryWebApi": { 19 | "commandName": "Project", 20 | "launchBrowser": false, 21 | "applicationUrl": "https://localhost:4001;http://localhost:4000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseWebApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:52995", 7 | "sslPort": 44347 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "PurchaseWebApi": { 19 | "commandName": "Project", 20 | "launchBrowser": false, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace SalesWebApi 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/Purchase.QueryHandler/Purchase.QueryHandler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure/QueryBus.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Infrastructure 2 | { 3 | using Common.Core; 4 | using System; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | public class QueryBus : IQueryBus 9 | { 10 | private readonly IServiceProvider serviceProvider; 11 | 12 | public QueryBus(IServiceProvider serviceProvider) 13 | { 14 | this.serviceProvider = serviceProvider; 15 | } 16 | 17 | public async Task Send(TQuery query) 18 | where TQuery : IQuery 19 | where TResult : QueryResult 20 | { 21 | var queryHandler = serviceProvider.GetService>(); 22 | 23 | return await queryHandler.Handle(query); 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseWebApi/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Api 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Tests/Inventory.Api.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesQueryHandler/SalesQueryHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.QueryHandler 2 | { 3 | using Common.Core; 4 | using Sales.DTO; 5 | using Sales.Query; 6 | using Sales.Repository; 7 | using System.Threading.Tasks; 8 | 9 | public class SalesQueryHandler : IQueryHandler 10 | { 11 | private readonly ISalesRepository salesRepository; 12 | 13 | public SalesQueryHandler(ISalesRepository salesRepository) 14 | { 15 | this.salesRepository = salesRepository; 16 | } 17 | 18 | public async Task Handle(SalesQuery query) 19 | { 20 | ProductSalesDTO productSalesDTO = await salesRepository.FindSoldProductInfo(query.ProductId, query.DateFrom, query.DateTo); 21 | 22 | return new QueryResult { Result = productSalesDTO }; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | EXPOSE 443 7 | 8 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build 9 | WORKDIR /src 10 | COPY ["SalesMicroservice/SalesWebApi/SalesWebApi.csproj", "SalesMicroservice/SalesWebApi/"] 11 | RUN dotnet restore "SalesMicroservice/SalesWebApi/SalesWebApi.csproj" 12 | COPY . . 13 | WORKDIR "/src/SalesMicroservice/SalesWebApi" 14 | RUN dotnet build "SalesWebApi.csproj" -c Release -o /app/build 15 | 16 | FROM build AS publish 17 | RUN dotnet publish "SalesWebApi.csproj" -c Release -o /app/publish 18 | 19 | FROM base AS final 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "SalesWebApi.dll"] -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseQuery/ProductPurchasedQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Query 2 | { 3 | using Common.Core; 4 | using System; 5 | 6 | public class ProductPurchasedQuery : IQuery 7 | { 8 | public DateTime DateFrom { get; set; } 9 | 10 | public DateTime DateTo { get; set; } 11 | 12 | /// 13 | /// Page stared from 1 14 | /// 15 | public int PageNumber { get; set; } 16 | 17 | /// 18 | /// No of records per page 19 | /// 20 | public int PageSize { get; set; } 21 | 22 | /// 23 | /// Field Name to be ordered 24 | /// 25 | public string SortFiled { get; set; } 26 | 27 | /// 28 | /// 0 -> Ascending 1 -> Descending 29 | /// 30 | public int SortDirection { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesCommand/SalesCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Command 2 | { 3 | using Common.Core; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | public class SalesCommand : ICommand 9 | { 10 | public string SalesId { get; set; } 11 | 12 | public DateTime SalesDate { get; set; } 13 | 14 | public IList SalesProducts { get; set; } 15 | 16 | public SalesCommand() 17 | { 18 | this.SalesProducts = new List(); 19 | } 20 | } 21 | 22 | public class SalesProduct 23 | { 24 | public string ProductId { get; set; } 25 | 26 | public string ProductName { get; set; } 27 | 28 | public string UnitName { get; set; } 29 | 30 | public long SalesUnitPrice { get; set; } 31 | 32 | public long SalesQuantity { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure.Tests/Common.Infrastructure.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc.Tests/InventoryServiceTest.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api.Grpc.Tests 2 | { 3 | using FluentAssertions; 4 | using global::Grpc.Net.Client; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using static Inventory.Api.Grpc.InventoryServiceProvider; 8 | 9 | public class InventoryServiceTest 10 | { 11 | [Fact] 12 | public async Task ShouldReturnTotalItemsCountWhenValidStoreId() 13 | { 14 | using GrpcChannel channel = GrpcChannel.ForAddress("https://localhost:7001"); 15 | 16 | var client = new InventoryServiceProviderClient(channel); 17 | 18 | var request = new ServiceRequest 19 | { 20 | StoreId = "S001" 21 | }; 22 | 23 | ServiceReplay replay = await client.CountTotalItemsAsync(request); 24 | 25 | replay.ItemCount.Should().Equals("999"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | EXPOSE 443 7 | 8 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build 9 | WORKDIR /src 10 | COPY ["InventoryMicroservice/Inventory.Api.Grpc/Inventory.Api.Grpc.csproj", "InventoryMicroservice/Inventory.Api.Grpc/"] 11 | RUN dotnet restore "InventoryMicroservice/Inventory.Api.Grpc/Inventory.Api.Grpc.csproj" 12 | COPY . . 13 | WORKDIR "/src/InventoryMicroservice/Inventory.Api.Grpc" 14 | RUN dotnet build "Inventory.Api.Grpc.csproj" -c Release -o /app/build 15 | 16 | FROM build AS publish 17 | RUN dotnet publish "Inventory.Api.Grpc.csproj" -c Release -o /app/publish 18 | 19 | FROM base AS final 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "Inventory.Api.Grpc.dll"] -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure/CommandBus.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Infrastructure 2 | { 3 | using Common.Core; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | /// 9 | /// Delegate commands with its handler with the help of IOC 10 | /// 11 | public class CommandBus : ICommandBus 12 | { 13 | private readonly IServiceProvider serviceProvider; 14 | 15 | public CommandBus(IServiceProvider serviceProvider) 16 | { 17 | this.serviceProvider = serviceProvider; 18 | } 19 | 20 | public async Task Send(TCommand command) 21 | where TCommand : ICommand 22 | where TResult : CommandResult 23 | { 24 | var commandHandler = serviceProvider.GetService>(); 25 | 26 | return await commandHandler.Handle(command); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Hosting; 8 | 9 | namespace Inventory.Api.Grpc 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | CreateHostBuilder(args).Build().Run(); 16 | } 17 | 18 | // Additional configuration is required to successfully run gRPC on macOS. 19 | // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 20 | public static IHostBuilder CreateHostBuilder(string[] args) => 21 | Host.CreateDefaultBuilder(args) 22 | .ConfigureWebHostDefaults(webBuilder => 23 | { 24 | webBuilder.UseStartup(); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/IMongoService.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core 2 | { 3 | using MongoDB.Driver; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | public interface IMongoService 9 | { 10 | /// 11 | /// If provide null instead of databasename it will provide default database 12 | /// 13 | /// 14 | /// 15 | IMongoDatabase GetDatabase(string databaseName); 16 | 17 | /// 18 | /// Convenstion that followed -> Collection name would be pluralize from its type name. ex: UserRole -> UserRoles 19 | /// 20 | /// 21 | /// 22 | /// 23 | IMongoCollection GetCollection(); 24 | 25 | IMongoCollection GetCollection(string collectionName, string databaseName); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:53205", 7 | "sslPort": 44354 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SalesWebApi": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:6001;http://localhost:6000", 24 | "launchBrowser": false 25 | }, 26 | "Docker": { 27 | "commandName": "Docker", 28 | "launchBrowser": false, 29 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 30 | "publishAllPorts": true, 31 | "useSSL": true 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | 11 | namespace Inventory.Api.Grpc 12 | { 13 | public class Startup 14 | { 15 | public void ConfigureServices(IServiceCollection services) 16 | { 17 | services.AddGrpc(); 18 | } 19 | 20 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 21 | { 22 | if (env.IsDevelopment()) 23 | { 24 | app.UseDeveloperExceptionPage(); 25 | } 26 | 27 | app.UseRouting(); 28 | 29 | app.UseEndpoints(endpoints => 30 | { 31 | endpoints.MapGrpcService(); 32 | }); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/Controllers/StoreQueryController.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api.Controllers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Common.Core; 8 | using Inventory.Query; 9 | using Microsoft.AspNetCore.Http; 10 | using Microsoft.AspNetCore.Mvc; 11 | 12 | [Route("api/[controller]")] 13 | [ApiController] 14 | public class StoreQueryController : ControllerBase 15 | { 16 | private readonly IQueryBus queryBus; 17 | 18 | public StoreQueryController(IQueryBus queryBus) 19 | { 20 | this.queryBus = queryBus; 21 | } 22 | 23 | [HttpGet("stores")] 24 | public Task GetAllStores() 25 | { 26 | return queryBus.Send(new StoreQuery()); 27 | } 28 | 29 | [HttpGet("is-service-on")] 30 | public Task IsServiceOn() 31 | { 32 | return Task.FromResult(true); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/Controllers/StoreCommandController.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api.Controllers 2 | { 3 | using Common.Core; 4 | using Inventory.Command; 5 | using Inventory.DTO; 6 | using Microsoft.AspNetCore.Mvc; 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | [Route("api/[controller]")] 11 | [ApiController] 12 | public class StoreCommandController : ControllerBase 13 | { 14 | private readonly ICommandBus commandBus; 15 | 16 | public StoreCommandController(ICommandBus commandBus) 17 | { 18 | this.commandBus = commandBus; 19 | } 20 | 21 | [HttpPost] 22 | [Route("[action]")] 23 | public Task CreateStore(StoreDTO store) 24 | { 25 | var command = new CreateStoreCommand 26 | { 27 | StoreId = Guid.NewGuid().ToString(), 28 | ManagerName = store.ManagerName 29 | }; 30 | 31 | return commandBus.Send(command); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/Events/ProductPurchasedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core.Events 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public class ProductPurchasedEvent : IEvent 7 | { 8 | public string PurchaseId { get; set; } 9 | 10 | public DateTime PurchaseDate { get; set; } 11 | 12 | public IList LineItems { get; set; } 13 | 14 | public ProductPurchasedEvent() 15 | { 16 | LineItems = new List(); 17 | } 18 | } 19 | 20 | public class PurchasedLineItem 21 | { 22 | public string ProductId { get; set; } 23 | 24 | public string ProductName { get; set; } 25 | 26 | public long PurchasedQuantity { get; set; } 27 | 28 | public string PurchasedUnitName { get; set; } 29 | 30 | public long PurchasedUnitPrice { get; set; } 31 | 32 | public long PurchaedTotalPrice 33 | { 34 | get 35 | { 36 | return PurchasedUnitPrice * PurchasedQuantity; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/Sales.Repository.Tests/Sales.Repository.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 s. m. ahasan habib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/Controllers/SalesQueryController.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Api.Controllers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Common.Core; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Sales.Query; 11 | 12 | [Route("api/[controller]")] 13 | [ApiController] 14 | public class SalesQueryController : ControllerBase 15 | { 16 | private readonly IQueryBus queryBus; 17 | 18 | public SalesQueryController(IQueryBus queryBus) 19 | { 20 | this.queryBus = queryBus; 21 | } 22 | 23 | [HttpGet("sales")] 24 | public Task GetProudctSalesInfo(string productId, DateTime dateFrom, DateTime dateTo) 25 | { 26 | var salesQuery = new SalesQuery 27 | { 28 | ProductId = productId, 29 | DateFrom = dateFrom, 30 | DateTo = dateTo 31 | }; 32 | 33 | return queryBus.Send(salesQuery); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Tests/TestClientProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api.Tests 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.TestHost; 5 | using Microsoft.Extensions.Configuration; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Net.Http; 10 | using System.Text; 11 | 12 | public class TestClientProvider 13 | { 14 | public HttpClient HttpClient { get; private set; } 15 | 16 | public TestClientProvider() 17 | { 18 | var webHostBuilder = new WebHostBuilder(); 19 | webHostBuilder.UseConfiguration(GetConfig()); 20 | webHostBuilder.UseStartup(); 21 | 22 | HttpClient = new TestServer(webHostBuilder).CreateClient(); 23 | } 24 | 25 | private IConfiguration GetConfig() 26 | { 27 | var builder = new ConfigurationBuilder() 28 | .SetBasePath(Directory.GetCurrentDirectory()) 29 | .AddJsonFile("appsettings.json", true, true) 30 | .AddEnvironmentVariables(); 31 | 32 | return builder.Build(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Core/Events/ProductSoldEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Core.Events 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | public class ProductSoldEvent : IEvent 8 | { 9 | public string SalesId { get; set; } 10 | 11 | public DateTime SalesDate { get; set; } 12 | 13 | public IList ProductSoldLineItems { get; set; } 14 | 15 | public ProductSoldEvent() 16 | { 17 | ProductSoldLineItems = new List(); 18 | } 19 | } 20 | 21 | /// 22 | /// Part of ProductSoldEvent 23 | /// 24 | public class ProductSoldLineItem 25 | { 26 | public string ProductId { get; set; } 27 | 28 | public string ProductName { get; set; } 29 | 30 | public long SoldQuantity { get; set; } 31 | 32 | public string SoldUnitName { get; set; } 33 | 34 | public long SoldUnitPrice { get; set; } 35 | 36 | public long SoldTotalPrice 37 | { 38 | get 39 | { 40 | return SoldUnitPrice * SoldQuantity; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseWebApi/Purchase.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Grpc.Tests/Inventory.Api.Grpc.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.EventHandler/Inventory.EventHandler/ProductSoldEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.EventHandler 2 | { 3 | using Common.Core; 4 | using Common.Core.Events; 5 | using Inventory.Domain; 6 | using Inventory.Repository; 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | public class ProductSoldEventHandler : IEventHandler 11 | { 12 | private readonly IStoreItemRepository storeItemRepository; 13 | 14 | public ProductSoldEventHandler(IStoreItemRepository storeItemRepository) 15 | { 16 | this.storeItemRepository = storeItemRepository; 17 | } 18 | 19 | public async Task Handle(ProductSoldEvent @event) 20 | { 21 | foreach (ProductSoldLineItem soldItem in @event.ProductSoldLineItems) 22 | { 23 | StoreItem storeItem = await storeItemRepository.GetById(soldItem.ProductId); 24 | if (null == storeItem) 25 | throw new ApplicationException($"Sorry! StoreItem: {soldItem.ProductId} not found in store."); 26 | 27 | storeItem.BalanceQuantity -= soldItem.SoldQuantity; 28 | 29 | await storeItemRepository.UpdateItem(storeItem); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Tests/StoredCommandControllerTest.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api.Tests 2 | { 3 | using FluentAssertions; 4 | using Inventory.DTO; 5 | using Newtonsoft.Json; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | 11 | public class StoredCommandControllerTest 12 | { 13 | private readonly HttpClient httpClient; 14 | 15 | public StoredCommandControllerTest() 16 | { 17 | var testServerProvider = new TestClientProvider(); 18 | this.httpClient = testServerProvider.HttpClient; 19 | } 20 | 21 | [Fact] 22 | public async Task ShouldCreateStore() 23 | { 24 | var storeDTO = new StoreDTO 25 | { 26 | StoreId = "S001", 27 | ManagerName = "Mr. Manager" 28 | }; 29 | 30 | var storeJson = JsonConvert.SerializeObject(storeDTO); 31 | var content = new StringContent(storeJson, Encoding.UTF8, "application/json"); 32 | 33 | var response = await httpClient.PostAsync("/api/StoreCommand/CreateStore", content); 34 | 35 | response.EnsureSuccessStatusCode(); 36 | 37 | response.IsSuccessStatusCode.Should().BeTrue(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.Api.Tests/StoreQueryControllerTest.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api.Tests 2 | { 3 | using Common.Core; 4 | using FluentAssertions; 5 | using Inventory.Domain; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | using System.Collections.Generic; 9 | using System.Net.Http; 10 | using System.Threading.Tasks; 11 | using Xunit; 12 | 13 | public class StoreQueryControllerTest 14 | { 15 | private readonly HttpClient httpClient; 16 | 17 | public StoreQueryControllerTest() 18 | { 19 | httpClient = new TestClientProvider().HttpClient; 20 | } 21 | 22 | [Fact] 23 | public async Task ShouldFetchAllStoreInfo() 24 | { 25 | HttpResponseMessage responseMessage = await httpClient.GetAsync("/api/storequery/stores"); 26 | responseMessage.EnsureSuccessStatusCode(); 27 | 28 | var storesJson = await responseMessage.Content.ReadAsStringAsync(); 29 | var queryResult = JsonConvert.DeserializeObject(storesJson); 30 | 31 | queryResult.Succeed.Should().BeTrue(); 32 | var storeList = ((JArray) queryResult.Result).ToObject>(); 33 | 34 | storeList.Should().HaveCountGreaterOrEqualTo(1); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/Inventory.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseWebApi/Controllers/PurchaseQueryController.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Api.Controllers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Common.Core; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Purchase.DTO; 11 | using Purchase.Query; 12 | 13 | [Route("api/[controller]")] 14 | [ApiController] 15 | public class PurchaseQueryController : ControllerBase 16 | { 17 | private readonly IQueryBus queryBus; 18 | 19 | public PurchaseQueryController(IQueryBus queryBus) 20 | { 21 | this.queryBus = queryBus; 22 | } 23 | 24 | [HttpGet("purchase")] 25 | public Task GetPurchasedData([FromQuery] PurchaseQueryDTO query) 26 | { 27 | var productPurchaseQuery = new ProductPurchasedQuery 28 | { 29 | DateFrom = query.DateFrom, 30 | DateTo = query.DateTo, 31 | PageNumber = query.PageNumber, 32 | PageSize = query.PageSize, 33 | SortFiled = query.SortField, 34 | SortDirection = query.SortDirection 35 | }; 36 | 37 | return queryBus.Send(productPurchaseQuery); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseInfrastructure/RabbitMqBus.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Infrastructure 2 | { 3 | using Common.All; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using RabbitMQ.Client; 9 | using System.Text.Json; 10 | 11 | public class RabbitMqBus : IServiceBus 12 | { 13 | private readonly IDictionary settings; 14 | 15 | public RabbitMqBus(IDictionary settings) 16 | { 17 | this.settings = settings; 18 | } 19 | 20 | public Task Publish(string message, string queue) 21 | { 22 | PublishLocal(message, queue); 23 | 24 | return Task.CompletedTask; 25 | } 26 | 27 | private void PublishLocal(string message, string queue) 28 | { 29 | var factory = new ConnectionFactory 30 | { 31 | HostName = settings["host"], 32 | Port = Convert.ToInt32(settings["port"]) 33 | }; 34 | using var connection = factory.CreateConnection(); 35 | using var model = connection.CreateModel(); 36 | 37 | QueueDeclareOk ok = model.QueueDeclare(queue, true, false, false, null); 38 | byte[] bodyBytes = Encoding.UTF8.GetBytes(message); 39 | 40 | model.BasicPublish(string.Empty, queue, null, bodyBytes); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure/EmailService.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Infrastructure 2 | { 3 | using Common.Core; 4 | using MailKit.Net.Smtp; 5 | using MimeKit; 6 | using System; 7 | using System.Diagnostics; 8 | using System.Threading.Tasks; 9 | 10 | public class EmailService : IEmailService 11 | { 12 | private readonly EmailSettings settings; 13 | 14 | public EmailService(EmailSettings settings) 15 | { 16 | this.settings = settings; 17 | } 18 | 19 | public async Task SendEmail(EmailParams emailParams) 20 | { 21 | var mimeMessage = new MimeMessage(); 22 | emailParams.ToList.ForEach(to => mimeMessage.To.Add(new MailboxAddress(to))); 23 | emailParams.CcList.ForEach(to => mimeMessage.Cc.Add(new MailboxAddress(to))); 24 | mimeMessage.From.Add(new MailboxAddress(settings.FromAddress)); 25 | mimeMessage.Subject = emailParams.Subject; 26 | mimeMessage.Body = new TextPart("plain") { Text = emailParams.Body }; 27 | 28 | using var smtpClient = new SmtpClient(); 29 | 30 | try 31 | { 32 | smtpClient.Connect(settings.Host, settings.Port, settings.Ssl); 33 | await smtpClient.AuthenticateAsync(settings.UserId, settings.Password); 34 | 35 | await smtpClient.SendAsync(mimeMessage); 36 | } 37 | catch(Exception ex) 38 | { 39 | Debug.WriteLine(ex); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.QueryHandler/StoreQueryHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.QueryHandler 2 | { 3 | using Common.Core; 4 | using Inventory.Domain; 5 | using Inventory.DTO; 6 | using Inventory.Query; 7 | using Inventory.Repository; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Threading.Tasks; 11 | 12 | public class StoreQueryHandler : IQueryHandler 13 | { 14 | public readonly IStoreRepository storeRepository; 15 | 16 | public StoreQueryHandler(IStoreRepository storeRepository) 17 | { 18 | this.storeRepository = storeRepository; 19 | } 20 | 21 | public async Task Handle(StoreQuery query) 22 | { 23 | IEnumerable stores = await storeRepository.GetAll(); 24 | 25 | return new QueryResult 26 | { 27 | Result = Map(stores) 28 | }; 29 | } 30 | 31 | private IEnumerable Map(IEnumerable stores) 32 | { 33 | var dtoList = new List(); 34 | 35 | foreach(Store store in stores) 36 | { 37 | var storeDto = new StoreDTO 38 | { 39 | StoreId = store.Id, 40 | ManagerName = store.Manager, 41 | CreatedDate = store.CreatedOn 42 | }; 43 | 44 | dtoList.Add(storeDto); 45 | } 46 | 47 | return dtoList; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryRepository/StoreRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Repository 2 | { 3 | using Common.Core; 4 | using Inventory.Domain; 5 | using MongoDB.Driver; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | 9 | public class StoreRepository : IStoreRepository 10 | { 11 | private readonly IMongoService mongoDbService; 12 | 13 | public StoreRepository(IMongoService mongoDbService) 14 | { 15 | this.mongoDbService = mongoDbService; 16 | } 17 | 18 | public async Task> GetAll() 19 | { 20 | IMongoCollection storeCollection = mongoDbService.GetCollection(); 21 | 22 | IAsyncCursor storeCursor = await storeCollection.FindAsync(_ => true); 23 | 24 | return storeCursor.ToEnumerable(); 25 | } 26 | 27 | public async Task GetById(string id) 28 | { 29 | IMongoCollection storeCollection = mongoDbService.GetCollection(); 30 | 31 | IAsyncCursor storeCursor = await storeCollection.FindAsync(item => item.Id.Equals(id)); 32 | 33 | return await storeCursor.FirstOrDefaultAsync() ?? new Store { Id = id}; 34 | } 35 | 36 | public async Task Save(Store entity) 37 | { 38 | IMongoCollection storeCollection = mongoDbService.GetCollection(); 39 | 40 | await storeCollection.InsertOneAsync(entity); 41 | 42 | return entity; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/Purchase.QueryHandler/ProductPurchaseQueryHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.QueryHandler 2 | { 3 | using Common.Core; 4 | using Purchase.Query; 5 | using Purchase.Repository; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | 9 | public class ProductPurchaseQueryHandler : IQueryHandler 10 | { 11 | private readonly IPurchaseRepostiory purchaseRepository; 12 | 13 | public ProductPurchaseQueryHandler(IPurchaseRepostiory purchaseRepository) 14 | { 15 | this.purchaseRepository = purchaseRepository; 16 | } 17 | 18 | public async Task Handle(ProductPurchasedQuery query) 19 | { 20 | QueryResult queryResult = await ValidateQuery(query); 21 | 22 | if (!queryResult.Succeed) 23 | { 24 | return queryResult; 25 | } 26 | 27 | queryResult.Result = await purchaseRepository.SearchPurchases(query.DateFrom, query.DateTo, query.PageNumber, query.PageSize, query.SortFiled, query.SortDirection); 28 | 29 | 30 | return queryResult; 31 | } 32 | 33 | private Task ValidateQuery(ProductPurchasedQuery query) 34 | { 35 | var queryResult = new QueryResult(); 36 | 37 | if (query.DateFrom > query.DateTo) 38 | { 39 | queryResult.Error = $"Sorry! DateFrom should not be greter than DateTo"; 40 | } 41 | 42 | return Task.FromResult(queryResult); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/Sales.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6f3f0e8f-fb88-4e5c-b9ef-0aa62f52e827 6 | Linux 7 | ..\.. 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure/MongoService.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Infrastructure 2 | { 3 | using Common.Core; 4 | using MongoDB.Driver; 5 | using System; 6 | 7 | public class MongoService : IMongoService 8 | { 9 | private readonly MongoUrlBuilder mongoUrlBuilder; 10 | 11 | public MongoService(string connectionUrl) 12 | { 13 | this.mongoUrlBuilder = new MongoUrlBuilder(connectionUrl); 14 | } 15 | 16 | public IMongoCollection GetCollection() 17 | { 18 | string collectionName = $"{typeof(T).Name}s"; 19 | var mongoClient = new MongoClient(mongoUrlBuilder.ToMongoUrl()); 20 | IMongoDatabase mongoDatabase = mongoClient.GetDatabase(mongoUrlBuilder.DatabaseName); 21 | IMongoCollection collection = mongoDatabase.GetCollection(collectionName); 22 | 23 | return collection; 24 | } 25 | 26 | public IMongoCollection GetCollection(string collectionName, string databaseName) 27 | { 28 | var mongoClient = new MongoClient(mongoUrlBuilder.ToMongoUrl()); 29 | IMongoDatabase mongoDatabase = mongoClient.GetDatabase(databaseName); 30 | IMongoCollection collection = mongoDatabase.GetCollection(collectionName); 31 | 32 | return collection; 33 | } 34 | 35 | public IMongoDatabase GetDatabase(string databaseName) 36 | { 37 | var mongoClient = new MongoClient(mongoUrlBuilder.ToMongoUrl()); 38 | IMongoDatabase mongoDatabase = mongoClient.GetDatabase(databaseName); 39 | 40 | return mongoDatabase; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/Controllers/SalesCommandController.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Api.Controllers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Common.Core; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Sales.Command; 11 | using Sales.DTO; 12 | 13 | [Route("api/[controller]")] 14 | [ApiController] 15 | public class SalesCommandController : ControllerBase 16 | { 17 | private readonly ICommandBus commandBus; 18 | 19 | public SalesCommandController(ICommandBus commandBus) 20 | { 21 | this.commandBus = commandBus; 22 | } 23 | 24 | [HttpPost("sales")] 25 | public Task ProductSales([FromBody] SalesDTO salesDTO) 26 | { 27 | var salesCommand = new SalesCommand 28 | { 29 | SalesId = Guid.NewGuid().ToString(), 30 | SalesDate = salesDTO.SalesDate 31 | }; 32 | foreach(SalesProductDTO productDTO in salesDTO.SalesProducts) 33 | { 34 | var product = new SalesProduct 35 | { 36 | ProductId = productDTO.ProductId, 37 | ProductName = productDTO.ProductName, 38 | UnitName = productDTO.UnitName, 39 | SalesUnitPrice = productDTO.UnitPrice, 40 | SalesQuantity = productDTO.Quantity 41 | }; 42 | salesCommand.SalesProducts.Add(product); 43 | } 44 | 45 | return commandBus.Send(salesCommand); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryCommandHandler/CreateStoreCommandHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.CommandHandler 2 | { 3 | using Common.Core; 4 | using Inventory.Command; 5 | using Inventory.Domain; 6 | using Inventory.Repository; 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | public class CreateStoreCommandHandler : ICommandHandler 11 | { 12 | private readonly IStoreRepository storeRepository; 13 | 14 | public CreateStoreCommandHandler(IStoreRepository storeRepository) 15 | { 16 | this.storeRepository = storeRepository; 17 | } 18 | 19 | public async Task Handle(CreateStoreCommand command) 20 | { 21 | CommandResult commandResponse = ValidateCommand(command); 22 | if (!commandResponse.Succeed) 23 | { 24 | return commandResponse; 25 | } 26 | Store store = Map(command); 27 | await storeRepository.Save(store); 28 | 29 | return commandResponse; 30 | } 31 | 32 | private CommandResult ValidateCommand(CreateStoreCommand command) 33 | { 34 | var commandResponse = new CommandResult(); 35 | 36 | if (string.IsNullOrEmpty(command.ManagerName)) 37 | { 38 | commandResponse.Error = "Sorry! Manager name should not be empty."; 39 | } 40 | 41 | return commandResponse; 42 | } 43 | 44 | private Store Map(CreateStoreCommand command) 45 | { 46 | return new Store 47 | { 48 | Id = command.StoreId, 49 | Manager = command.ManagerName 50 | }; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCommandHandler/Purchase.CommandHandler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | all 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/Inventory.EventHandler/Inventory.EventHandler/ProductPurchasedEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.EventHandler 2 | { 3 | using Common.Core; 4 | using Common.Core.Events; 5 | using Inventory.Domain; 6 | using Inventory.Repository; 7 | using System.Threading.Tasks; 8 | 9 | public class ProductPurchasedEventHandler : IEventHandler 10 | { 11 | private readonly IStoreItemRepository storeItemRepository; 12 | private readonly IStoreRepository storeRepository; 13 | 14 | public ProductPurchasedEventHandler(IStoreItemRepository storeItemRepository, IStoreRepository storeRepository) 15 | { 16 | this.storeItemRepository = storeItemRepository; 17 | this.storeRepository = storeRepository; 18 | } 19 | 20 | public async Task Handle(ProductPurchasedEvent @event) 21 | { 22 | foreach (PurchasedLineItem purchasedItem in @event.LineItems) 23 | { 24 | StoreItem storeItem = await storeItemRepository.GetById(purchasedItem.ProductId); 25 | 26 | if (null == storeItem) 27 | { 28 | Store store = await storeRepository.GetById("S001"); 29 | 30 | var newStoreItem = new StoreItem 31 | { 32 | Id = purchasedItem.ProductId, 33 | ItemName = purchasedItem.ProductName, 34 | Store = store, 35 | BalanceQuantity = purchasedItem.PurchasedQuantity 36 | }; 37 | 38 | await storeItemRepository.Save(newStoreItem); 39 | } 40 | else 41 | { 42 | storeItem.BalanceQuantity += purchasedItem.PurchasedQuantity; 43 | 44 | await storeItemRepository.UpdateItem(storeItem); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseWebApi/Controllers/PurchaseCommandController.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Api.Controllers 2 | { 3 | using Common.Core; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Purchase.Command; 6 | using Purchase.DTO; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | 11 | [Route("api/[controller]")] 12 | [ApiController] 13 | public class PurchaseCommandController : ControllerBase 14 | { 15 | private readonly ICommandBus commandBus; 16 | 17 | public PurchaseCommandController(ICommandBus commandBus) 18 | { 19 | this.commandBus = commandBus; 20 | } 21 | 22 | [HttpPost("purchase")] 23 | public Task Purchase([FromBody] PurchaseDTO purchaseDTO) 24 | { 25 | var command = new PurchaseCommand 26 | { 27 | PurchaseId = Guid.NewGuid().ToString(), 28 | PurchaseDate = purchaseDTO.PurchaseDate 29 | }; 30 | command.LineItems = new List(); 31 | 32 | foreach (PurchaseItemDTO purchaseItemDTO in purchaseDTO.PurchaseItems ?? new List()) 33 | { 34 | command.LineItems.Add(new LineItemCommand 35 | { 36 | ProductId = purchaseItemDTO.ProductId, 37 | ProductName = purchaseItemDTO.ProductName, 38 | PurchaseQuantity = purchaseItemDTO.Quantity, 39 | PurchaseUnitName = purchaseItemDTO.UnitName, 40 | PurchaseUnitPrice = purchaseItemDTO.UnitPrice 41 | }); 42 | } 43 | 44 | return commandBus.Send(command); 45 | } 46 | 47 | [HttpPost("ping")] 48 | [HttpGet("ping")] 49 | public string Ping() 50 | { 51 | return new Random().Next().ToString(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/Sales.Repository.Tests/SalesRepositoryTest.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Repository.Tests 2 | { 3 | using Common.Infrastructure; 4 | using FluentAssertions; 5 | using Sales.Domain; 6 | using System; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | using Sales = Domain.Sales; 10 | 11 | public class SalesRepositoryTest 12 | { 13 | private readonly SalesRepository salesRepository; 14 | 15 | public SalesRepositoryTest() 16 | { 17 | const string connectionUrl = "mongodb://localhost:27017/SalesDB"; 18 | var mongoDbService = new MongoService(connectionUrl); 19 | this.salesRepository = new SalesRepository(mongoDbService); 20 | } 21 | 22 | [Fact] 23 | public async Task ShouldSaveSales() 24 | { 25 | var sales = new Sales 26 | { 27 | Id = Guid.NewGuid().ToString(), 28 | SalesDate = DateTime.UtcNow, 29 | }; 30 | 31 | sales.SalesLineItems.Add(new SalesLineItem 32 | { 33 | ProductId = "P001", 34 | ProductName = "Product-P001", 35 | UnitName = "Piece", 36 | UnitSalePrice = 100, 37 | SalesQuantity = 10 38 | }); 39 | sales.SalesLineItems.Add(new SalesLineItem 40 | { 41 | ProductId = "P002", 42 | ProductName = "Product-P002", 43 | UnitName = "Piece", 44 | UnitSalePrice = 200, 45 | SalesQuantity = 2 46 | }); 47 | 48 | Sales resultSales = await salesRepository.Save(sales); 49 | 50 | resultSales.Should().NotBeNull(); 51 | resultSales.SalesLineItems.Should().HaveCount(2); 52 | } 53 | 54 | [Fact] 55 | public async Task ShouldReturnSalesWhenValidId() 56 | { 57 | const string salesId = "6978887e-955f-4d38-9925-379348a29a8d"; 58 | 59 | Sales sales = await salesRepository.GetById(salesId); 60 | 61 | sales.Should().NotBeNull(); 62 | sales.Id.Should().BeEquivalentTo(salesId); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryRepository/StoreItemRepository.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Inventory.Repository 3 | { 4 | using Common.Core; 5 | using Inventory.Domain; 6 | using MongoDB.Driver; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | public class StoreItemRepository : IStoreItemRepository 11 | { 12 | private readonly IMongoService mongoService; 13 | 14 | public StoreItemRepository(IMongoService mongoDbService) 15 | { 16 | this.mongoService = mongoDbService; 17 | } 18 | 19 | public async Task GetById(string id) 20 | { 21 | IMongoCollection storeItemCollection = mongoService.GetCollection(); 22 | 23 | IAsyncCursor itemCursor = await storeItemCollection.FindAsync(item => item.Id.Equals(id)); 24 | 25 | return await itemCursor.FirstOrDefaultAsync(); 26 | } 27 | 28 | public async Task RemoveItem(string id) 29 | { 30 | IMongoCollection storeItemCollection = mongoService.GetCollection(); 31 | 32 | await storeItemCollection.DeleteOneAsync(item => item.Id.Equals(id)); 33 | } 34 | 35 | public async Task Save(StoreItem entity) 36 | { 37 | IMongoCollection storeItemCollection = mongoService.GetCollection(); 38 | 39 | await storeItemCollection.InsertOneAsync(entity); 40 | 41 | return entity; 42 | } 43 | 44 | public async Task SaveItems(IEnumerable entities) 45 | { 46 | foreach (StoreItem item in entities) 47 | { 48 | await Save(item); 49 | } 50 | } 51 | 52 | public async Task UpdateItem(StoreItem entity) 53 | { 54 | IMongoCollection storeItemCollection = mongoService.GetCollection(); 55 | FilterDefinition criterial = Builders.Filter.Eq(item => item.Id, entity.Id); 56 | 57 | await storeItemCollection.ReplaceOneAsync(criterial, entity); 58 | 59 | return entity; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesRepository/SalesRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.Repository 2 | { 3 | using Common.Core; 4 | using MongoDB.Driver; 5 | using Sales.DTO; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | using Sales = Domain.Sales; 10 | using System.Linq; 11 | using Sales.Domain; 12 | 13 | public class SalesRepository : ISalesRepository 14 | { 15 | private readonly IMongoService mongoDbService; 16 | 17 | public SalesRepository(IMongoService mongoDbService) 18 | { 19 | this.mongoDbService = mongoDbService; 20 | } 21 | 22 | public async Task GetById(string id) 23 | { 24 | IMongoCollection salesCollection = mongoDbService.GetCollection(); 25 | 26 | IAsyncCursor salesCursor = await salesCollection.FindAsync(item => item.Id.Equals(id)); 27 | 28 | return await salesCursor.FirstOrDefaultAsync(); 29 | } 30 | 31 | public async Task Save(Sales entity) 32 | { 33 | IMongoCollection salesCollection = mongoDbService.GetCollection(); 34 | 35 | await salesCollection.InsertOneAsync(entity); 36 | 37 | return entity; 38 | } 39 | 40 | public Task FindSoldProductInfo(string productId, DateTime dateFrom, DateTime dateTo) 41 | { 42 | var resultDic = new Dictionary(); 43 | IMongoCollection salesCollection = mongoDbService.GetCollection(); 44 | IEnumerable salesList = salesCollection.AsQueryable().Where(item => item.SalesDate >= dateFrom && item.SalesDate <= dateTo); 45 | 46 | long totalSalesQuantity = 0; 47 | string productName = string.Empty; 48 | 49 | foreach (Sales sales in salesList) 50 | { 51 | IEnumerable SalesLineItems = sales.SalesLineItems.Where(item => item.ProductId.Equals(productId)); 52 | 53 | foreach (SalesLineItem item in SalesLineItems) 54 | { 55 | totalSalesQuantity += item.SalesQuantity; 56 | if (string.IsNullOrEmpty(productName)) 57 | { 58 | productName = item.ProductName; 59 | } 60 | } 61 | } 62 | 63 | var proudctSalesDTO = new ProductSalesDTO 64 | { 65 | ProductId = productId, 66 | ProductName = productName, 67 | SalesQuantity = totalSalesQuantity 68 | }; 69 | 70 | return Task.FromResult(proudctSalesDTO); 71 | } 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure.Tests/RabbitMqServiceBusTest.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Infrastructure.Tests 2 | { 3 | using Common.Core; 4 | using Common.Core.Events; 5 | using FluentAssertions; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Moq; 8 | using Moq.Language.Flow; 9 | using RabbitMQ.Client; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Threading.Tasks; 13 | using Xunit; 14 | 15 | public class RabbitMqServiceBusTest 16 | { 17 | private readonly MessageBrokerSettings rabbitMqSettings; 18 | private readonly EventBus rabbitMqServiceBus; 19 | private const string QueueName = "product-purchased"; 20 | 21 | public RabbitMqServiceBusTest() 22 | { 23 | rabbitMqSettings = new MessageBrokerSettings 24 | { 25 | Host = "127.0.0.1", 26 | Port = 5672, 27 | UserId = "guest", 28 | Password = "guest" 29 | }; 30 | 31 | var serviceProvider = new Mock(); 32 | serviceProvider.Setup(srv => srv.GetService(typeof(MessageBrokerSettings))).Returns(rabbitMqSettings); 33 | serviceProvider.Setup(srv => srv.GetService(typeof(ISerializer))).Returns(new JsonSerializer()); 34 | serviceProvider.Setup(item => item.GetService(typeof(IEventHandler))) 35 | .Returns(new PurchaseEventHandler()); 36 | 37 | rabbitMqServiceBus = new EventBus(serviceProvider.Object); 38 | } 39 | 40 | [Fact] 41 | public async Task ShouldPublishMessageWhenValidQueueProvided() 42 | { 43 | var lineItems = new List 44 | { 45 | new PurchasedLineItem{ProductId = Guid.NewGuid().ToString(), PurchasedUnitPrice = 100, PurchasedQuantity = 100}, 46 | new PurchasedLineItem{ProductId = Guid.NewGuid().ToString(), PurchasedUnitPrice = 200, PurchasedQuantity = 200}, 47 | }; 48 | 49 | var productPurchasedEvent = new ProductPurchasedEvent 50 | { 51 | PurchaseDate = DateTime.UtcNow, 52 | LineItems = lineItems 53 | }; 54 | 55 | await rabbitMqServiceBus.Publish(QueueName, productPurchasedEvent); 56 | } 57 | 58 | [Fact] 59 | public async Task ShouldReceiveMessageWhenValidQueueProvided() 60 | { 61 | await rabbitMqServiceBus.Subscribe(QueueName); 62 | 63 | await Task.Delay(1000 * 3); 64 | } 65 | } 66 | 67 | public class PurchaseEventHandler : IEventHandler 68 | { 69 | public async Task Handle(ProductPurchasedEvent @event) 70 | { 71 | @event.Should().NotBeNull(); 72 | 73 | await Task.CompletedTask; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseRepository/PurchaseRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Repository 2 | { 3 | using Common.Core; 4 | using MongoDB.Driver; 5 | using Purchase.Domain.Model; 6 | using Purchase.DTO; 7 | using Purchase.Repository; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Threading.Tasks; 12 | 13 | public class PurchaseRepository : IPurchaseRepostiory 14 | { 15 | private readonly IMongoService mongoDbService; 16 | 17 | public PurchaseRepository(IMongoService mongoDbService) 18 | { 19 | this.mongoDbService = mongoDbService; 20 | } 21 | 22 | public async Task GetById(string id) 23 | { 24 | IMongoCollection purchaseCollection = mongoDbService.GetCollection(); 25 | 26 | IAsyncCursor purchaseCursor = await purchaseCollection.FindAsync(item => item.Id.Equals(id)); 27 | 28 | return await purchaseCursor.FirstOrDefaultAsync(); 29 | } 30 | 31 | public async Task Save(Purchase purchase) 32 | { 33 | IMongoCollection purchaseCollection = mongoDbService.GetCollection(); 34 | 35 | await purchaseCollection.InsertOneAsync(purchase); 36 | 37 | return purchase; 38 | } 39 | 40 | public Task> SearchPurchases(DateTime from, DateTime to, int pageNumber, int pageSize, string sortField, int sortDirection) 41 | { 42 | IMongoCollection purchaseCollection = mongoDbService.GetCollection(); 43 | 44 | int skip = (pageNumber - 1) * pageSize; 45 | 46 | IQueryable query = purchaseCollection.AsQueryable().Where(item => item.PurchaseDate >= from && item.PurchaseDate <= to); 47 | 48 | var orderBy = sortDirection == 0 ? query.OrderBy(item => sortField) : query.OrderByDescending(item => sortField); 49 | 50 | IEnumerable purchases = query.Skip(skip).Take(pageSize); 51 | 52 | IEnumerable productPurchasedDtoList = Map(purchases); 53 | 54 | return Task.FromResult(productPurchasedDtoList); 55 | } 56 | 57 | private IEnumerable Map(IEnumerable purchases) 58 | { 59 | var dtoList = new List(); 60 | 61 | foreach (Purchase purchase in purchases) 62 | { 63 | foreach (ProductLineItem productLineItem in purchase.LineItems) 64 | { 65 | var productPurchasedDTO = new ProductPurchasedDTO 66 | { 67 | Date = purchase.PurchaseDate, 68 | ProductName = productLineItem.Product.ProductName, 69 | PurchasedQuantity = productLineItem.PurchaseQuantity, 70 | PurchasedAmount = productLineItem.PurchaseTotalPrice 71 | }; 72 | 73 | dtoList.Add(productPurchasedDTO); 74 | } 75 | } 76 | 77 | return dtoList; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesCommandHandler/SalesCommandHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Sales.CommandHandler 2 | { 3 | using Common.Core; 4 | using Common.Core.Events; 5 | using Sales.Command; 6 | using Sales.Core; 7 | using Sales.Domain; 8 | using Sales.Repository; 9 | using System.Threading.Tasks; 10 | using Sales = Sales.Domain.Sales; 11 | 12 | public class SalesCommandHandler : ICommandHandler 13 | { 14 | private readonly ISalesRepository salesRepository; 15 | private readonly IEventBus eventBus; 16 | 17 | public SalesCommandHandler(ISalesRepository salesRepository, IEventBus eventBus) 18 | { 19 | this.salesRepository = salesRepository; 20 | this.eventBus = eventBus; 21 | } 22 | 23 | public async Task Handle(SalesCommand command) 24 | { 25 | CommandResult commandResult = await ValidateCommand(command); 26 | if (!commandResult.Succeed) 27 | { 28 | return commandResult; 29 | } 30 | 31 | Sales sales = Map(command); 32 | await salesRepository.Save(sales); 33 | 34 | ProductSoldEvent productSoldEvent = MapEvent(command); 35 | await eventBus.Publish(Constants.MessageQueue.SalesQueue, productSoldEvent); 36 | 37 | return commandResult; 38 | 39 | } 40 | 41 | private Task ValidateCommand(SalesCommand command) 42 | { 43 | var commandResult = new CommandResult(); 44 | 45 | if (command.SalesProducts.Count == 0) 46 | { 47 | commandResult.Error = "Sorry! Atlease 1 item should be present"; 48 | } 49 | 50 | return Task.FromResult(commandResult); 51 | } 52 | 53 | private Sales Map(SalesCommand command) 54 | { 55 | var sales = new Sales 56 | { 57 | Id = command.SalesId, 58 | SalesDate = command.SalesDate 59 | }; 60 | foreach(SalesProduct product in command.SalesProducts) 61 | { 62 | var salesLineItem = new SalesLineItem 63 | { 64 | ProductId = product.ProductId, 65 | ProductName = product.ProductName, 66 | SalesQuantity = product.SalesQuantity, 67 | UnitName = product.UnitName, 68 | UnitSalePrice = product.SalesUnitPrice 69 | }; 70 | sales.SalesLineItems.Add(salesLineItem); 71 | } 72 | 73 | return sales; 74 | } 75 | 76 | private ProductSoldEvent MapEvent(SalesCommand command) 77 | { 78 | var productSoldEvent = new ProductSoldEvent 79 | { 80 | SalesId = command.SalesId, 81 | SalesDate = command.SalesDate 82 | }; 83 | foreach (SalesProduct product in command.SalesProducts) 84 | { 85 | var proudctSoldLineItem = new ProductSoldLineItem 86 | { 87 | ProductId = product.ProductId, 88 | ProductName = product.ProductName, 89 | SoldQuantity = product.SalesQuantity, 90 | SoldUnitPrice = product.SalesUnitPrice 91 | }; 92 | productSoldEvent.ProductSoldLineItems.Add(proudctSoldLineItem); 93 | } 94 | 95 | return productSoldEvent; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Src/SalesMicroservice/SalesWebApi/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace SalesWebApi 2 | { 3 | using Common.Core; 4 | using Common.Infrastructure; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | using Sales.Command; 11 | using Sales.CommandHandler; 12 | using Sales.Query; 13 | using Sales.QueryHandler; 14 | using Sales.Repository; 15 | 16 | public class Startup 17 | { 18 | private readonly IConfiguration configRoot; 19 | 20 | public Startup(IConfiguration configRoot) 21 | { 22 | this.configRoot = configRoot; 23 | } 24 | 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | services.AddMvc(); 28 | services.AddControllers(); 29 | 30 | RegisterServices(services); 31 | } 32 | 33 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 34 | { 35 | if (env.IsDevelopment()) 36 | { 37 | app.UseDeveloperExceptionPage(); 38 | } 39 | 40 | app.UseRouting(); 41 | 42 | app.UseEndpoints(endpoints => 43 | { 44 | endpoints.MapDefaultControllerRoute(); 45 | }); 46 | } 47 | 48 | private void RegisterServices(IServiceCollection services) 49 | { 50 | var fi = new System.IO.FileInfo(@$"{System.IO.Directory.GetCurrentDirectory()}\appsettings.json"); 51 | var ff = fi.OpenText().ReadToEnd(); 52 | 53 | RegisterHelperServices(services); 54 | RegisterBuses(services); 55 | RegisterCommandHandlers(services); 56 | RegisterEventHandlers(services); 57 | RegisterQueryHandlers(services); 58 | RegisterRepositories(services); 59 | 60 | } 61 | 62 | private void RegisterHelperServices(IServiceCollection services) 63 | { 64 | var rabbitMqSettings = new MessageBrokerSettings 65 | { 66 | Host = "127.0.0.1", 67 | Port = 5672, 68 | UserId = "guest", 69 | Password = "guest" 70 | }; 71 | services.AddSingleton(rabbitMqSettings); 72 | services.AddSingleton(new MongoService(configRoot.GetConnectionString("Default"))); 73 | 74 | services.AddSingleton(); 75 | } 76 | 77 | private void RegisterBuses(IServiceCollection services) 78 | { 79 | services.AddSingleton(); 80 | services.AddSingleton(); 81 | services.AddSingleton(); 82 | } 83 | 84 | private void RegisterCommandHandlers(IServiceCollection services) 85 | { 86 | services.AddSingleton, SalesCommandHandler>(); 87 | } 88 | 89 | private void RegisterEventHandlers(IServiceCollection services) 90 | { 91 | } 92 | 93 | private void RegisterQueryHandlers(IServiceCollection services) 94 | { 95 | services.AddSingleton, SalesQueryHandler>(); 96 | } 97 | 98 | private void RegisterRepositories(IServiceCollection services) 99 | { 100 | services.AddSingleton(); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Src/InventoryMicroservice/InventoryWebApi/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory.Api 2 | { 3 | using Common.Core; 4 | using Common.Core.Events; 5 | using Common.Infrastructure; 6 | using Inventory.Command; 7 | using Inventory.CommandHandler; 8 | using Inventory.EventHandler; 9 | using Inventory.Query; 10 | using Inventory.QueryHandler; 11 | using Inventory.Repository; 12 | using Microsoft.AspNetCore.Builder; 13 | using Microsoft.AspNetCore.Hosting; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Hosting; 17 | using System.Threading.Tasks; 18 | 19 | public class Startup 20 | { 21 | public IConfiguration Configuration { get; private set; } 22 | 23 | public Startup(IConfiguration configRoot) 24 | { 25 | this.Configuration = configRoot; 26 | } 27 | 28 | public void ConfigureServices(IServiceCollection services) 29 | { 30 | services.AddMvc(); 31 | services.AddControllers(); 32 | RegisterServices(services); 33 | } 34 | 35 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 36 | { 37 | if (env.IsDevelopment()) 38 | { 39 | app.UseDeveloperExceptionPage(); 40 | } 41 | 42 | app.UseRouting(); 43 | 44 | app.UseEndpoints(endpoints => 45 | { 46 | endpoints.MapDefaultControllerRoute(); 47 | }); 48 | } 49 | 50 | private void RegisterServices(IServiceCollection services) 51 | { 52 | RegisterHelperServices(services); 53 | RegisterCommandHanders(services); 54 | RegisterQueryHandlers(services); 55 | RegisterEventHandlers(services); 56 | RegisterRepositories(services); 57 | RegisterBuses(services).Wait(); 58 | } 59 | 60 | private void RegisterHelperServices(IServiceCollection services) 61 | { 62 | var rabbitMqSettings = new MessageBrokerSettings 63 | { 64 | Host = "127.0.0.1", 65 | Port = 5672, 66 | UserId = "guest", 67 | Password = "guest" 68 | }; 69 | services.AddSingleton(rabbitMqSettings); 70 | services.AddSingleton(new MongoService(Configuration.GetConnectionString("Default"))); 71 | services.AddSingleton(); 72 | } 73 | 74 | private async Task RegisterBuses(IServiceCollection services) 75 | { 76 | services.AddSingleton(); 77 | services.AddSingleton(); 78 | 79 | var eventBus = new EventBus(services.BuildServiceProvider()); 80 | await eventBus.Subscribe(Constants.MessageQueue.PurchaseQueue); 81 | await eventBus.Subscribe(Constants.MessageQueue.SalesQueue); 82 | services.AddSingleton(eventBus); 83 | } 84 | 85 | private void RegisterCommandHanders(IServiceCollection services) 86 | { 87 | services.AddSingleton, CreateStoreCommandHandler>(); 88 | } 89 | 90 | private void RegisterQueryHandlers(IServiceCollection services) 91 | { 92 | services.AddSingleton, StoreQueryHandler>(); 93 | } 94 | 95 | private void RegisterEventHandlers(IServiceCollection services) 96 | { 97 | services.AddSingleton, ProductPurchasedEventHandler>(); 98 | services.AddSingleton, ProductSoldEventHandler>(); 99 | } 100 | 101 | private void RegisterRepositories(IServiceCollection services) 102 | { 103 | services.AddSingleton(); 104 | services.AddSingleton(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseWebApi/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.Api 2 | { 3 | using Common.Core; 4 | using Common.Infrastructure; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | using Purchase.Command; 11 | using Purchase.CommandHandler; 12 | using Purchase.Query; 13 | using Purchase.QueryHandler; 14 | using Purchase.Repository; 15 | using System; 16 | 17 | public class Startup 18 | { 19 | private readonly IConfiguration configRoot; 20 | 21 | public Startup(IConfiguration configRoot) 22 | { 23 | this.configRoot = configRoot; 24 | } 25 | 26 | public void ConfigureServices(IServiceCollection services) 27 | { 28 | services.AddMvc(); 29 | services.AddControllers(); 30 | services.AddHttpClient(); 31 | 32 | RegisterServices(services); 33 | } 34 | 35 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 36 | { 37 | if (env.IsDevelopment()) 38 | { 39 | app.UseDeveloperExceptionPage(); 40 | } 41 | 42 | app.UseRouting(); 43 | 44 | app.UseEndpoints(endpoints => 45 | { 46 | endpoints.MapControllerRoute( 47 | name: "default", 48 | pattern: "{controller}/{action}/{id?}"); 49 | }); 50 | } 51 | 52 | private void RegisterServices(IServiceCollection serviceCollection) 53 | { 54 | RegisterHelperServices(serviceCollection); 55 | RegisterBuses(serviceCollection); 56 | RegisterCommandHandlers(serviceCollection); 57 | RegisterQueryHandlers(serviceCollection); 58 | RegisterEventHanders(serviceCollection); 59 | RegisterRepositories(serviceCollection); 60 | } 61 | 62 | private void RegisterRepositories(IServiceCollection services) 63 | { 64 | services.AddSingleton(); 65 | } 66 | 67 | private void RegisterCommandHandlers(IServiceCollection services) 68 | { 69 | services.AddSingleton, PurchaseCommandHandler>(); 70 | } 71 | 72 | private void RegisterQueryHandlers(IServiceCollection services) 73 | { 74 | services.AddSingleton, ProductPurchaseQueryHandler>(); 75 | } 76 | 77 | private void RegisterEventHanders(IServiceCollection services) 78 | { 79 | 80 | } 81 | 82 | private void RegisterBuses(IServiceCollection services) 83 | { 84 | services.AddSingleton(); 85 | services.AddSingleton(); 86 | services.AddSingleton(); 87 | } 88 | 89 | private void RegisterHelperServices(IServiceCollection services) 90 | { 91 | services.AddSingleton(item => 92 | { 93 | string connectionString = configRoot.GetConnectionString("default"); 94 | return new MongoService(connectionString); 95 | }); 96 | 97 | services.AddSingleton(); 98 | 99 | services.AddSingleton(item => 100 | { 101 | IConfigurationSection emailSettings = configRoot.GetSection("EmailSettings"); 102 | var settings = new EmailSettings 103 | { 104 | Host = emailSettings["Host"], 105 | Port = int.Parse(emailSettings["Port"]), 106 | UserId = emailSettings["UserId"], 107 | Password = emailSettings["Password"] 108 | }; 109 | return new EmailService(settings); 110 | }); 111 | 112 | var rabbitMqSettings = new MessageBrokerSettings 113 | { 114 | Host = "127.0.0.1", 115 | Port = 5672, 116 | UserId = "guest", 117 | Password = "guest" 118 | }; 119 | services.AddSingleton(rabbitMqSettings); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Src/CommonAll/Common.Infrastructure/EventBus.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Infrastructure 2 | { 3 | using Common.Core; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using RabbitMQ.Client; 6 | using RabbitMQ.Client.Events; 7 | using System; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | /// 12 | /// RabbitMQ implementaiton 13 | /// 14 | public class EventBus : IEventBus 15 | { 16 | private readonly IServiceProvider serviceProvider; 17 | private readonly IConnection connection; 18 | private readonly IModel channel; 19 | private readonly EventingBasicConsumer consumer; 20 | 21 | public EventBus(IServiceProvider serviceProvider) 22 | { 23 | this.serviceProvider = serviceProvider; 24 | 25 | ConnectionFactory connectionFactory = CreateRabbitMqConnectionFactory(); 26 | connection = connectionFactory.CreateConnection(); 27 | channel = connection.CreateModel(); 28 | consumer = new EventingBasicConsumer(channel); 29 | } 30 | 31 | public async Task Publish(string queue, T @event) where T : IEvent 32 | { 33 | var serializer = serviceProvider.GetRequiredService(); 34 | string json = await serializer.Serialize(@event); 35 | byte[] messageBytes = Encoding.UTF8.GetBytes(json); 36 | 37 | await PublishInner(queue, messageBytes); 38 | } 39 | 40 | public async Task Subscribe(string queue) where T : IEvent 41 | { 42 | await SubscribeInner(channel, queue); 43 | } 44 | 45 | private Task PublishInner(string queue, byte[] messageBytes) 46 | { 47 | string exchangeName = CreateExchangeName(queue); 48 | string queueName = CreateQueueName(queue); 49 | 50 | channel.ExchangeDeclare(exchangeName, "fanout"); 51 | channel.QueueDeclare(queueName, true, false, false, null); 52 | channel.QueueBind(queueName, exchangeName, queueName); 53 | 54 | channel.BasicPublish(exchangeName, queueName, null, messageBytes); 55 | 56 | return Task.CompletedTask; 57 | } 58 | 59 | private Task SubscribeInner(IModel channel, string queue) where T:IEvent 60 | { 61 | string queueName = CreateQueueName(queue); 62 | string exchangeName = CreateExchangeName(queue); 63 | 64 | channel.ExchangeDeclare(exchangeName, "fanout"); 65 | channel.QueueDeclare(queueName, true, false, false, null); 66 | channel.QueueBind(queueName, exchangeName, queueName); 67 | 68 | consumer.Received += async (sender, e) => 69 | { 70 | byte[] bytes = e.Body.ToArray(); 71 | string messageJson = Encoding.UTF8.GetString(bytes); 72 | var serializer = serviceProvider.GetRequiredService(); 73 | T messageObject = await serializer.Decerialize(messageJson); 74 | var eventHandler = serviceProvider.GetService>(); 75 | 76 | await eventHandler.Handle(messageObject); 77 | }; 78 | 79 | channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumer); 80 | 81 | return Task.CompletedTask; 82 | } 83 | 84 | private ConnectionFactory CreateRabbitMqConnectionFactory() 85 | { 86 | var settings = serviceProvider.GetRequiredService(); 87 | 88 | return new ConnectionFactory 89 | { 90 | HostName = settings.Host, 91 | Port = settings.Port, 92 | UserName = settings.UserId, 93 | Password = settings.Password 94 | }; 95 | } 96 | 97 | /// 98 | /// Modifed Queue name 99 | /// 100 | /// 101 | /// 102 | private string CreateQueueName(string queueName) 103 | { 104 | return $"{queueName}-queue"; 105 | } 106 | 107 | /// 108 | /// Create exchange name from QueueName 109 | /// 110 | /// 111 | /// 112 | private string CreateExchangeName(string queueName) 113 | { 114 | return $"{queueName}-exchange"; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | *.cache 25 | # Visual Studio 2015 cache/options directory 26 | src/.vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | *.cache 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | #*.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.jfm 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # FAKE - F# Make 251 | .fake/ 252 | 253 | # JetBrains Rider 254 | .idea/ 255 | *.sln.iml 256 | 257 | # CodeRush 258 | .cr/ 259 | 260 | # Python Tools for Visual Studio (PTVS) 261 | __pycache__/ 262 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TheMicroServices 2 | 3 | Demonastrate Event Driven Microservice based architecture. 4 | 5 | 6 | ## Domain: 7 | 8 | Borrowed 3 Boundned context from ERP: 9 | 10 | 1. **Purchase**: User can purchase Item. Purchase department store that purchased item information. 11 | 2. **Sales**: After saling items, user can store sales information 12 | 3. **Inventory**: When any purchase or Sales occured, Stock will be automatically updated. 13 | 14 | 15 | ## Architecture: 16 | 17 | System is consist of 3 Microservices: 18 | 19 | * **Purchase Microservice**: This microservice is responsible to manage Purchase related informtion and raised an event after purchase done. Based on the event, 20 | consumer will be updated accordingly. 21 | 22 | * **Sales Microservice**: This microservice is responsible to manage Sales related information. When any sales happend it will raised an event so that its interested 23 | party can update and act accordingly. 24 | 25 | * **Inventory Microservice**: This microservice is responsible to manage Products and also when any purchase or sales happended it will update stock based on their raised events. 26 | 27 | * **CQRS**: CQRS is implemented inside Microservices. 28 | 29 | * **Nothing shared architecture**. Every Microservice has its own database. 30 | 31 | 32 | Here, I have shouwn 3 ways to communicate Microservice to Microservice communication: 33 | 34 | * **Event Driven**: Any microservice Publish events to the Message Queue. Other Microservices subscribe those events and act accordingly. 35 | * **REST**: Not recommended approach. But showing here as an example. One Microservice Directly call other Micrservice to the REST way. 36 | * **GRPC**: Recommended approach for now a days. It is very similar like old Remote Procedure Call(RPC). 37 | 38 | 39 | #### High Level Design: 40 | 41 | ![High Level](https://github.com/habibsql/TheMicroservices/blob/main/Docs/highlevel.JPG?raw=true) 42 | 43 | #### Command/Query Segrigation: 44 | 45 | * In CQRS command and query are two distinct parts. Here Read Model and Write Model are different. 46 | * Command execution change the application state. So command execution flows are risky by nature and very carefully need to implement. 47 | * Query execution does not change anything just read the data. So little relux but if application contain sensitive data than 48 | it might be risky too. 49 | 50 | ![CommandQuery](https://github.com/habibsql/TheMicroservices/blob/main/Docs/cq.JPG?raw=true) 51 | 52 | 53 | ## Event Driven Architecture: 54 | 55 | It is difference than traditional Request driven model. In event driven architecture one system emit events, other systems 56 | capture those events and react accordingly. The system communicate, processing based on event flows. Someone called 57 | it as Publisher subscriber Model. 58 | 59 | ![Event-Driven](https://github.com/habibsql/TheMicroservices/blob/main/Docs/ed.JPG?raw=true) 60 | 61 | ## CAP theorem: 62 | 63 | It is also called brewer's theorem. It says whether any distributed system either achieve at most 2 out of 3 (It is actually proved).By cleaverly design so many systems in real world, if has achieved all 3 based on some tricks or you can say made balanced between them. It is business decission rather technical decission. 64 | 65 | #### 3 Parts are: 66 | * **C**onsistency: All clients must see the same data from all differnce places. 67 | * **A**vailability: When any client requests for data, he/she can get data immediately, event 1 or 2 or mnore nodes are down. 68 | * **P**artitioning: ommunication break within a distributed system. 69 | 70 | ![CAP](https://github.com/habibsql/TheMicroservices/blob/main/Docs/cap.JPG?raw=true) 71 | 72 | ## Eventual Consistency: 73 | 74 | It name described data is replicated other sources gradually. It is weekest consistency but many benifits, one of is easy scaling & best performace. For that reason many distributed system 75 | use this and sacrifice strong consistency. Its sequence is: 76 | **1**. Write node-1 from client 77 | **2**. Acknoledge to client from node-1 78 | **3**. Eventual write propagates other nodes via cluster. 79 | 80 | ![CAP](https://github.com/habibsql/TheMicroservices/blob/main/Docs/ec.JPG?raw=true) 81 | 82 | ## Circuit Breaker/Retry Policy 83 | 84 | Circuit breaker and retry policy are two important things in Microservice world. Here It assume that network is unreliable and may lost its 85 | connection any time. It is a major issue here. So solve this issue and build a system resilece 2 patters play important role. 86 | 87 | * **Retry Policy**: Allow how many times (after time interval) a service call. 88 | 89 | * **Circuit Breaker Policy**: Idea is very simple. We should wrap any function with Circuit breaker object and circuit breaker object monitor its failure. 90 | When failure happend at cirtain time interval, circuit breaker trips all futher call with a error message. It has 3 states: 91 | 92 | * **i)Open**: Returns an error for calls without executing functions. 93 | * **ii)Closed**: Call all the services. 94 | * **iii)Half-Open**: After timeperiod, any issue raised then check underlying problem still exists or not. Any single call failed half CB changed to half open state. 95 | 96 | ![Circuit-Breaker](https://github.com/habibsql/TheMicroservices/blob/main/Docs/cb.JPG?raw=true) 97 | 98 | ## Technology Used: 99 | 100 | * C# Language 101 | * ASP.NET Core Framework. 102 | * RabbitMQ: Message Broker for event pub/sub feature. 103 | * MongoDB: NoSQL database (Prefefer NoSQL instead of SQL for better schemaless/scalability/speed etc. 104 | * Rest/GRPC: Service-to-service communication. 105 | * Polly: A Nuget package for setting Auto Retry Remote call settings. 106 | * TestHost: (Microsoft.AspNetCore.TestHost) for WebAPI integration test. 107 | 108 | ## Demonastration: 109 | 110 | * Synchronous Event Driven with Service Broker 111 | * CQRS 112 | * NOSQL 113 | * Service to Service communication with REST with Retry Policy 114 | * Service to Service communication with GRPC with Circuit Breaker Policy 115 | * Web API service Integration Test 116 | -------------------------------------------------------------------------------- /Src/PurchaseMicroservice/PurchaseCommandHandler/PurchaseCommandHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Purchase.CommandHandler 2 | { 3 | using Common.Core; 4 | using Common.Core.Events; 5 | using Grpc.Core; 6 | using Grpc.Net.Client; 7 | using Inventory.Api.Grpc; 8 | using Polly; 9 | using Polly.CircuitBreaker; 10 | using Polly.Retry; 11 | using Purchase.Command; 12 | using Purchase.Core; 13 | using Purchase.Domain.Model; 14 | using Purchase.Repository; 15 | using System; 16 | using System.Diagnostics; 17 | using System.Linq; 18 | using System.Net.Http; 19 | using System.Threading.Tasks; 20 | using static Inventory.Api.Grpc.InventoryServiceProvider; 21 | 22 | public class PurchaseCommandHandler : ICommandHandler 23 | { 24 | private readonly IPurchaseRepostiory purchaseRepository; 25 | private readonly IEventBus serviceBus; 26 | private readonly IEmailService emailService; 27 | private IHttpClientFactory httpClientFactory; 28 | private AsyncCircuitBreakerPolicy circuitAsyncBreakerPolicy; 29 | 30 | public PurchaseCommandHandler(IPurchaseRepostiory purchaseRepository, IEventBus serviceBus, IEmailService emailService, 31 | IHttpClientFactory httpClientFactory) 32 | { 33 | this.purchaseRepository = purchaseRepository; 34 | this.serviceBus = serviceBus; 35 | this.emailService = emailService; 36 | this.httpClientFactory = httpClientFactory; 37 | 38 | // Circuit breaker policy defined 39 | circuitAsyncBreakerPolicy = Policy 40 | .Handle().CircuitBreakerAsync(2, TimeSpan.FromSeconds(15)); 41 | } 42 | 43 | public async Task Handle(PurchaseCommand purchaseCommand) 44 | { 45 | CommandResult commandResponse = ValidateCommand(purchaseCommand); 46 | //if (!commandResponse.Succeed) 47 | //{ 48 | // return commandResponse; 49 | //} 50 | //if (!await IsStoreServiceOn()) // REST call with auto retry policy 51 | //{ 52 | // commandResponse.Error = "Sorry! Store Service is not On. You have to wait until it is open"; 53 | 54 | // return commandResponse; 55 | //} 56 | if (await GetCurrentStoreItems() > 1000L) // GRPC call with Circuit breaker policy 57 | { 58 | commandResponse.Error = "Sorry! Current storeitems more than 1000. So no more purchase possible"; 59 | 60 | return commandResponse; 61 | } 62 | 63 | Purchase purchase = Map(purchaseCommand); 64 | await purchaseRepository.Save(purchase); 65 | 66 | ProductPurchasedEvent productPurchasedEvent = Map(purchase); 67 | await serviceBus.Publish(Constants.MessageQueues.PurchasedQueue, productPurchasedEvent); 68 | 69 | EmailParams emailParams = BuildEmailParameters(productPurchasedEvent); 70 | await emailService.SendEmail(emailParams); 71 | 72 | return new CommandResult(); 73 | } 74 | 75 | private CommandResult ValidateCommand(PurchaseCommand productPurchaseCommand) 76 | { 77 | var commandResponse = new CommandResult(); 78 | 79 | if (null == productPurchaseCommand) 80 | { 81 | commandResponse.Error = "Sorry! Product Purchase Command should not be null."; 82 | } 83 | else if (productPurchaseCommand.LineItems == null || productPurchaseCommand.LineItems.Count == 0) 84 | { 85 | commandResponse.Error = "Sorry! Should have at least one Purchase Item."; 86 | } 87 | 88 | return commandResponse; 89 | } 90 | 91 | private Purchase Map(PurchaseCommand purchaseCommand) 92 | { 93 | var purchase = new Purchase 94 | { 95 | Id = purchaseCommand.PurchaseId, 96 | PurchaseDate = DateTime.UtcNow 97 | }; 98 | 99 | foreach (LineItemCommand lineItemCommand in purchaseCommand.LineItems) 100 | { 101 | var productLineItem = new ProductLineItem 102 | { 103 | Product = new Product 104 | { 105 | Id = lineItemCommand.ProductId, 106 | ProductName = lineItemCommand.ProductName 107 | } 108 | }; 109 | productLineItem.PurchaseUnitPrice = lineItemCommand.PurchaseUnitPrice; 110 | productLineItem.PurchaseQuantity = lineItemCommand.PurchaseQuantity; 111 | 112 | purchase.LineItems.Add(productLineItem); 113 | } 114 | 115 | return purchase; 116 | } 117 | 118 | private ProductPurchasedEvent Map(Purchase purchase) 119 | { 120 | var productPurchasedEvent = new ProductPurchasedEvent 121 | { 122 | PurchaseDate = purchase.PurchaseDate 123 | }; 124 | 125 | foreach (ProductLineItem productLineItem in purchase.LineItems) 126 | { 127 | var lineItem = new PurchasedLineItem 128 | { 129 | ProductId = productLineItem.Product.Id, 130 | PurchasedUnitPrice = productLineItem.PurchaseUnitPrice, 131 | PurchasedQuantity = productLineItem.PurchaseQuantity 132 | }; 133 | productPurchasedEvent.LineItems.Add(lineItem); 134 | } 135 | 136 | return productPurchasedEvent; 137 | } 138 | 139 | private EmailParams BuildEmailParameters(ProductPurchasedEvent @event) 140 | { 141 | long purchasedTotalPrice = 0; 142 | 143 | @event.LineItems.ToList().ForEach(item => purchasedTotalPrice += item.PurchaedTotalPrice); 144 | 145 | var emailParams = new EmailParams 146 | { 147 | Subject = "Product Purchased", 148 | Body = $"The Product Purchased Amount={purchasedTotalPrice}" 149 | }; 150 | 151 | emailParams.ToList.Add(Constants.EmailAddressess.AdminPurchaseEmailAddress); 152 | 153 | return emailParams; 154 | } 155 | 156 | /// 157 | /// Implement Retry policy with REST call. 158 | /// Considering network is unreliable. so retry is needed. 159 | /// 160 | /// 161 | private async Task IsStoreServiceOn() 162 | { 163 | bool serviceOn = false; 164 | const string url = "http://localhost:4000/api/storequery/is-service-on"; 165 | HttpClient httpClient = httpClientFactory.CreateClient(); 166 | 167 | // Retry Policy Defined 168 | AsyncRetryPolicy retryPolicy 169 | = Policy.Handle().WaitAndRetryAsync(3, item => TimeSpan.FromSeconds(2)); 170 | try 171 | { 172 | await retryPolicy.ExecuteAsync(async () => 173 | { 174 | HttpResponseMessage response = await httpClient.GetAsync(url); 175 | response.EnsureSuccessStatusCode(); 176 | serviceOn = await response.Content.ReadAsAsync(); 177 | }); 178 | } 179 | catch (HttpRequestException) { } 180 | 181 | return serviceOn; 182 | } 183 | 184 | /// 185 | /// GRPC Call with Circuit breaker policy 186 | /// 187 | /// 188 | private async Task GetCurrentStoreItems() 189 | { 190 | const string url = "https://localhost:7001"; 191 | using GrpcChannel channel = GrpcChannel.ForAddress(url); 192 | var client = new InventoryServiceProviderClient(channel); 193 | var request = new ServiceRequest { StoreId = "S001" }; 194 | long totalItems = 0L; 195 | 196 | try 197 | { 198 | await circuitAsyncBreakerPolicy.ExecuteAsync(async () => 199 | { 200 | ServiceReplay replay = await client.CountTotalItemsAsync(request); 201 | totalItems = long.Parse(replay.ItemCount); 202 | }); 203 | } 204 | catch(RpcException rEx) 205 | { 206 | Debug.WriteLine($"Grpc Exception:{rEx.Message}"); 207 | await Task.Delay(1000); 208 | } 209 | 210 | return totalItems; 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Src/TheMicroservices.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PurchaseMicroservice", "PurchaseMicroservice", "{66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.Api", "PurchaseMicroservice\PurchaseWebApi\Purchase.Api.csproj", "{4A96FD48-1F32-4787-8914-CE61765E2D7B}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.Command", "PurchaseMicroservice\PurchaseCommand\Purchase.Command.csproj", "{AB5DD339-5F31-4B97-B883-8D87119A4D9C}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.Query", "PurchaseMicroservice\PurchaseQuery\Purchase.Query.csproj", "{37C9ABAA-E217-43C1-9E78-D45B721DA6B7}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.Core", "PurchaseMicroservice\PurchaseCore\Purchase.Core.csproj", "{ADF5C5A0-292A-47E8-933D-0E49159ECF8A}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.Repository", "PurchaseMicroservice\PurchaseRepository\Purchase.Repository.csproj", "{1E5B4E96-249C-4239-81E1-ECC45EC1E84E}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.Domain", "PurchaseMicroservice\PurchaseDomain\Purchase.Domain.csproj", "{B73A83B8-8E52-4CBA-AA33-19073A170AE1}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.DTO", "PurchaseMicroservice\PurchaseDto\Purchase.DTO.csproj", "{3293F4D5-E1A8-46BA-A1B3-B06095D16FBA}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SalesMicroservice", "SalesMicroservice", "{B5EA3087-36A6-4F1B-B57D-B4A0439552AF}" 23 | EndProject 24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InventoryMicroservice", "InventoryMicroservice", "{A520C293-BEE3-463F-A448-488C5EC15D24}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.Api", "InventoryMicroservice\InventoryWebApi\Inventory.Api.csproj", "{039921D8-4248-4929-9600-F6794AEB46E0}" 27 | EndProject 28 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.Command", "InventoryMicroservice\InventoryCommand\Inventory.Command.csproj", "{2242FC5A-384D-4644-85C0-8D8AA1D7D2DB}" 29 | EndProject 30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.CommandHandler", "InventoryMicroservice\InventoryCommandHandler\Inventory.CommandHandler.csproj", "{DCE0ABDE-C706-4357-B2C2-508E5E5E4028}" 31 | EndProject 32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.Query", "InventoryMicroservice\InventoryQuery\Inventory.Query.csproj", "{F6BCF270-F199-4DD7-A1C3-D4132FDBDC03}" 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InventoryQueryHandler", "InventoryMicroservice\InventoryQueryHandler\InventoryQueryHandler.csproj", "{495F1E24-3A2E-44D1-BE21-07ABAD784B22}" 35 | EndProject 36 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.Domain", "InventoryMicroservice\InventoryDomain\Inventory.Domain.csproj", "{8B8EEB17-30F9-40D5-9C6B-BB80E0C5E5CC}" 37 | EndProject 38 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.Repository", "InventoryMicroservice\InventoryRepository\Inventory.Repository.csproj", "{9F7C7EF9-DFE1-4CF1-BBA4-E76618D973E8}" 39 | EndProject 40 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.DTO", "InventoryMicroservice\InventoryDto\Inventory.DTO.csproj", "{2B8CA8C4-B09F-4161-9687-14CCC589D1F4}" 41 | EndProject 42 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.Api", "SalesMicroservice\SalesWebApi\Sales.Api.csproj", "{39EA2A18-2DD0-4C9D-988F-E61EF77ADF1A}" 43 | EndProject 44 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.Core", "SalesMicroservice\SalesCore\Sales.Core.csproj", "{59677058-994E-487E-B294-EE9C71DA96D2}" 45 | EndProject 46 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.Command", "SalesMicroservice\SalesCommand\Sales.Command.csproj", "{16A1506C-A00D-450F-8782-43060B8C240B}" 47 | EndProject 48 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.CommandHandler", "SalesMicroservice\SalesCommandHandler\Sales.CommandHandler.csproj", "{5B2200C9-36FE-4D5B-BA73-B0171FAE6A01}" 49 | EndProject 50 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.Query", "SalesMicroservice\SalesQuery\Sales.Query.csproj", "{165E63CF-5C74-4DF5-A4B1-5883ED032F2C}" 51 | EndProject 52 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.QueryHandler", "SalesMicroservice\SalesQueryHandler\Sales.QueryHandler.csproj", "{80F6E25B-6F22-4D14-9C0D-9C6AEF30A5D1}" 53 | EndProject 54 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.Domain", "SalesMicroservice\SalesDomain\Sales.Domain.csproj", "{6D8DC78C-F1D1-43E6-B1FC-3DA5AE1CF8D2}" 55 | EndProject 56 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.Repository", "SalesMicroservice\SalesRepository\Sales.Repository.csproj", "{3E5DC4CA-772C-4FAF-8B11-CC3AECE210E0}" 57 | EndProject 58 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.DTO", "SalesMicroservice\SalesDto\Sales.DTO.csproj", "{2A91ABB0-3955-4E08-8C20-6231A2D0F17D}" 59 | EndProject 60 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CommonAll", "CommonAll", "{7A660546-3B3E-44B8-BF65-422CDA59BED4}" 61 | EndProject 62 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.CommandHandler", "PurchaseMicroservice\PurchaseCommandHandler\Purchase.CommandHandler.csproj", "{EF1FF2CC-BC36-497B-9D21-E0851909325F}" 63 | EndProject 64 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common.Infrastructure", "CommonAll\Common.Infrastructure\Common.Infrastructure.csproj", "{72DAEC1C-873C-43BC-B027-9FD67AF7E4B8}" 65 | EndProject 66 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common.Core", "CommonAll\Common.Core\Common.Core.csproj", "{B2ED16D4-F4CB-4B93-817C-C787213D8A6E}" 67 | EndProject 68 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.EventHandler", "InventoryMicroservice\Inventory.EventHandler\Inventory.EventHandler\Inventory.EventHandler.csproj", "{5028999F-F0EC-4421-A786-606372EC0B01}" 69 | EndProject 70 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common.Infrastructure.Tests", "CommonAll\Common.Infrastructure.Tests\Common.Infrastructure.Tests.csproj", "{E17DA84E-69C1-4EB9-AC24-39A9605259EE}" 71 | EndProject 72 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Purchase.QueryHandler", "PurchaseMicroservice\Purchase.QueryHandler\Purchase.QueryHandler.csproj", "{0D327170-0673-4A43-8A29-07912377A898}" 73 | EndProject 74 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common.Core.Tests", "CommonAll\Common.Core.Tests\Common.Core.Tests.csproj", "{DAE9ABA6-E347-4659-B579-D80A6AC2000E}" 75 | EndProject 76 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.QueryHandler", "InventoryMicroservice\Inventory.QueryHandler\Inventory.QueryHandler.csproj", "{1E79EA40-B395-4D11-AA12-A9ED08D18983}" 77 | EndProject 78 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales.Repository.Tests", "SalesMicroservice\Sales.Repository.Tests\Sales.Repository.Tests.csproj", "{5D8E595F-6C68-47DE-9D1A-538AA6720A02}" 79 | EndProject 80 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.Api.Grpc", "InventoryMicroservice\Inventory.Api.Grpc\Inventory.Api.Grpc.csproj", "{E904AF5C-35A2-40F9-8EF2-0FDE5390D6F6}" 81 | EndProject 82 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.Api.Grpc.Tests", "InventoryMicroservice\Inventory.Api.Grpc.Tests\Inventory.Api.Grpc.Tests.csproj", "{F1BF0BC9-38BC-432B-AB28-4BF68A1A344C}" 83 | EndProject 84 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inventory.Api.Tests", "InventoryMicroservice\Inventory.Api.Tests\Inventory.Api.Tests.csproj", "{FB7E260A-C146-40C3-BF7C-641BEF92A7A0}" 85 | EndProject 86 | Global 87 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 88 | Debug|Any CPU = Debug|Any CPU 89 | Release|Any CPU = Release|Any CPU 90 | EndGlobalSection 91 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 92 | {4A96FD48-1F32-4787-8914-CE61765E2D7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 93 | {4A96FD48-1F32-4787-8914-CE61765E2D7B}.Debug|Any CPU.Build.0 = Debug|Any CPU 94 | {4A96FD48-1F32-4787-8914-CE61765E2D7B}.Release|Any CPU.ActiveCfg = Release|Any CPU 95 | {4A96FD48-1F32-4787-8914-CE61765E2D7B}.Release|Any CPU.Build.0 = Release|Any CPU 96 | {AB5DD339-5F31-4B97-B883-8D87119A4D9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 97 | {AB5DD339-5F31-4B97-B883-8D87119A4D9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 98 | {AB5DD339-5F31-4B97-B883-8D87119A4D9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 99 | {AB5DD339-5F31-4B97-B883-8D87119A4D9C}.Release|Any CPU.Build.0 = Release|Any CPU 100 | {37C9ABAA-E217-43C1-9E78-D45B721DA6B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 101 | {37C9ABAA-E217-43C1-9E78-D45B721DA6B7}.Debug|Any CPU.Build.0 = Debug|Any CPU 102 | {37C9ABAA-E217-43C1-9E78-D45B721DA6B7}.Release|Any CPU.ActiveCfg = Release|Any CPU 103 | {37C9ABAA-E217-43C1-9E78-D45B721DA6B7}.Release|Any CPU.Build.0 = Release|Any CPU 104 | {ADF5C5A0-292A-47E8-933D-0E49159ECF8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 105 | {ADF5C5A0-292A-47E8-933D-0E49159ECF8A}.Debug|Any CPU.Build.0 = Debug|Any CPU 106 | {ADF5C5A0-292A-47E8-933D-0E49159ECF8A}.Release|Any CPU.ActiveCfg = Release|Any CPU 107 | {ADF5C5A0-292A-47E8-933D-0E49159ECF8A}.Release|Any CPU.Build.0 = Release|Any CPU 108 | {1E5B4E96-249C-4239-81E1-ECC45EC1E84E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 109 | {1E5B4E96-249C-4239-81E1-ECC45EC1E84E}.Debug|Any CPU.Build.0 = Debug|Any CPU 110 | {1E5B4E96-249C-4239-81E1-ECC45EC1E84E}.Release|Any CPU.ActiveCfg = Release|Any CPU 111 | {1E5B4E96-249C-4239-81E1-ECC45EC1E84E}.Release|Any CPU.Build.0 = Release|Any CPU 112 | {B73A83B8-8E52-4CBA-AA33-19073A170AE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 113 | {B73A83B8-8E52-4CBA-AA33-19073A170AE1}.Debug|Any CPU.Build.0 = Debug|Any CPU 114 | {B73A83B8-8E52-4CBA-AA33-19073A170AE1}.Release|Any CPU.ActiveCfg = Release|Any CPU 115 | {B73A83B8-8E52-4CBA-AA33-19073A170AE1}.Release|Any CPU.Build.0 = Release|Any CPU 116 | {3293F4D5-E1A8-46BA-A1B3-B06095D16FBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 117 | {3293F4D5-E1A8-46BA-A1B3-B06095D16FBA}.Debug|Any CPU.Build.0 = Debug|Any CPU 118 | {3293F4D5-E1A8-46BA-A1B3-B06095D16FBA}.Release|Any CPU.ActiveCfg = Release|Any CPU 119 | {3293F4D5-E1A8-46BA-A1B3-B06095D16FBA}.Release|Any CPU.Build.0 = Release|Any CPU 120 | {039921D8-4248-4929-9600-F6794AEB46E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 121 | {039921D8-4248-4929-9600-F6794AEB46E0}.Debug|Any CPU.Build.0 = Debug|Any CPU 122 | {039921D8-4248-4929-9600-F6794AEB46E0}.Release|Any CPU.ActiveCfg = Release|Any CPU 123 | {039921D8-4248-4929-9600-F6794AEB46E0}.Release|Any CPU.Build.0 = Release|Any CPU 124 | {2242FC5A-384D-4644-85C0-8D8AA1D7D2DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 125 | {2242FC5A-384D-4644-85C0-8D8AA1D7D2DB}.Debug|Any CPU.Build.0 = Debug|Any CPU 126 | {2242FC5A-384D-4644-85C0-8D8AA1D7D2DB}.Release|Any CPU.ActiveCfg = Release|Any CPU 127 | {2242FC5A-384D-4644-85C0-8D8AA1D7D2DB}.Release|Any CPU.Build.0 = Release|Any CPU 128 | {DCE0ABDE-C706-4357-B2C2-508E5E5E4028}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 129 | {DCE0ABDE-C706-4357-B2C2-508E5E5E4028}.Debug|Any CPU.Build.0 = Debug|Any CPU 130 | {DCE0ABDE-C706-4357-B2C2-508E5E5E4028}.Release|Any CPU.ActiveCfg = Release|Any CPU 131 | {DCE0ABDE-C706-4357-B2C2-508E5E5E4028}.Release|Any CPU.Build.0 = Release|Any CPU 132 | {F6BCF270-F199-4DD7-A1C3-D4132FDBDC03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 133 | {F6BCF270-F199-4DD7-A1C3-D4132FDBDC03}.Debug|Any CPU.Build.0 = Debug|Any CPU 134 | {F6BCF270-F199-4DD7-A1C3-D4132FDBDC03}.Release|Any CPU.ActiveCfg = Release|Any CPU 135 | {F6BCF270-F199-4DD7-A1C3-D4132FDBDC03}.Release|Any CPU.Build.0 = Release|Any CPU 136 | {495F1E24-3A2E-44D1-BE21-07ABAD784B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 137 | {495F1E24-3A2E-44D1-BE21-07ABAD784B22}.Debug|Any CPU.Build.0 = Debug|Any CPU 138 | {495F1E24-3A2E-44D1-BE21-07ABAD784B22}.Release|Any CPU.ActiveCfg = Release|Any CPU 139 | {495F1E24-3A2E-44D1-BE21-07ABAD784B22}.Release|Any CPU.Build.0 = Release|Any CPU 140 | {8B8EEB17-30F9-40D5-9C6B-BB80E0C5E5CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 141 | {8B8EEB17-30F9-40D5-9C6B-BB80E0C5E5CC}.Debug|Any CPU.Build.0 = Debug|Any CPU 142 | {8B8EEB17-30F9-40D5-9C6B-BB80E0C5E5CC}.Release|Any CPU.ActiveCfg = Release|Any CPU 143 | {8B8EEB17-30F9-40D5-9C6B-BB80E0C5E5CC}.Release|Any CPU.Build.0 = Release|Any CPU 144 | {9F7C7EF9-DFE1-4CF1-BBA4-E76618D973E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 145 | {9F7C7EF9-DFE1-4CF1-BBA4-E76618D973E8}.Debug|Any CPU.Build.0 = Debug|Any CPU 146 | {9F7C7EF9-DFE1-4CF1-BBA4-E76618D973E8}.Release|Any CPU.ActiveCfg = Release|Any CPU 147 | {9F7C7EF9-DFE1-4CF1-BBA4-E76618D973E8}.Release|Any CPU.Build.0 = Release|Any CPU 148 | {2B8CA8C4-B09F-4161-9687-14CCC589D1F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 149 | {2B8CA8C4-B09F-4161-9687-14CCC589D1F4}.Debug|Any CPU.Build.0 = Debug|Any CPU 150 | {2B8CA8C4-B09F-4161-9687-14CCC589D1F4}.Release|Any CPU.ActiveCfg = Release|Any CPU 151 | {2B8CA8C4-B09F-4161-9687-14CCC589D1F4}.Release|Any CPU.Build.0 = Release|Any CPU 152 | {39EA2A18-2DD0-4C9D-988F-E61EF77ADF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 153 | {39EA2A18-2DD0-4C9D-988F-E61EF77ADF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU 154 | {39EA2A18-2DD0-4C9D-988F-E61EF77ADF1A}.Release|Any CPU.ActiveCfg = Release|Any CPU 155 | {39EA2A18-2DD0-4C9D-988F-E61EF77ADF1A}.Release|Any CPU.Build.0 = Release|Any CPU 156 | {59677058-994E-487E-B294-EE9C71DA96D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 157 | {59677058-994E-487E-B294-EE9C71DA96D2}.Debug|Any CPU.Build.0 = Debug|Any CPU 158 | {59677058-994E-487E-B294-EE9C71DA96D2}.Release|Any CPU.ActiveCfg = Release|Any CPU 159 | {59677058-994E-487E-B294-EE9C71DA96D2}.Release|Any CPU.Build.0 = Release|Any CPU 160 | {16A1506C-A00D-450F-8782-43060B8C240B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 161 | {16A1506C-A00D-450F-8782-43060B8C240B}.Debug|Any CPU.Build.0 = Debug|Any CPU 162 | {16A1506C-A00D-450F-8782-43060B8C240B}.Release|Any CPU.ActiveCfg = Release|Any CPU 163 | {16A1506C-A00D-450F-8782-43060B8C240B}.Release|Any CPU.Build.0 = Release|Any CPU 164 | {5B2200C9-36FE-4D5B-BA73-B0171FAE6A01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 165 | {5B2200C9-36FE-4D5B-BA73-B0171FAE6A01}.Debug|Any CPU.Build.0 = Debug|Any CPU 166 | {5B2200C9-36FE-4D5B-BA73-B0171FAE6A01}.Release|Any CPU.ActiveCfg = Release|Any CPU 167 | {5B2200C9-36FE-4D5B-BA73-B0171FAE6A01}.Release|Any CPU.Build.0 = Release|Any CPU 168 | {165E63CF-5C74-4DF5-A4B1-5883ED032F2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 169 | {165E63CF-5C74-4DF5-A4B1-5883ED032F2C}.Debug|Any CPU.Build.0 = Debug|Any CPU 170 | {165E63CF-5C74-4DF5-A4B1-5883ED032F2C}.Release|Any CPU.ActiveCfg = Release|Any CPU 171 | {165E63CF-5C74-4DF5-A4B1-5883ED032F2C}.Release|Any CPU.Build.0 = Release|Any CPU 172 | {80F6E25B-6F22-4D14-9C0D-9C6AEF30A5D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 173 | {80F6E25B-6F22-4D14-9C0D-9C6AEF30A5D1}.Debug|Any CPU.Build.0 = Debug|Any CPU 174 | {80F6E25B-6F22-4D14-9C0D-9C6AEF30A5D1}.Release|Any CPU.ActiveCfg = Release|Any CPU 175 | {80F6E25B-6F22-4D14-9C0D-9C6AEF30A5D1}.Release|Any CPU.Build.0 = Release|Any CPU 176 | {6D8DC78C-F1D1-43E6-B1FC-3DA5AE1CF8D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 177 | {6D8DC78C-F1D1-43E6-B1FC-3DA5AE1CF8D2}.Debug|Any CPU.Build.0 = Debug|Any CPU 178 | {6D8DC78C-F1D1-43E6-B1FC-3DA5AE1CF8D2}.Release|Any CPU.ActiveCfg = Release|Any CPU 179 | {6D8DC78C-F1D1-43E6-B1FC-3DA5AE1CF8D2}.Release|Any CPU.Build.0 = Release|Any CPU 180 | {3E5DC4CA-772C-4FAF-8B11-CC3AECE210E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 181 | {3E5DC4CA-772C-4FAF-8B11-CC3AECE210E0}.Debug|Any CPU.Build.0 = Debug|Any CPU 182 | {3E5DC4CA-772C-4FAF-8B11-CC3AECE210E0}.Release|Any CPU.ActiveCfg = Release|Any CPU 183 | {3E5DC4CA-772C-4FAF-8B11-CC3AECE210E0}.Release|Any CPU.Build.0 = Release|Any CPU 184 | {2A91ABB0-3955-4E08-8C20-6231A2D0F17D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 185 | {2A91ABB0-3955-4E08-8C20-6231A2D0F17D}.Debug|Any CPU.Build.0 = Debug|Any CPU 186 | {2A91ABB0-3955-4E08-8C20-6231A2D0F17D}.Release|Any CPU.ActiveCfg = Release|Any CPU 187 | {2A91ABB0-3955-4E08-8C20-6231A2D0F17D}.Release|Any CPU.Build.0 = Release|Any CPU 188 | {EF1FF2CC-BC36-497B-9D21-E0851909325F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 189 | {EF1FF2CC-BC36-497B-9D21-E0851909325F}.Debug|Any CPU.Build.0 = Debug|Any CPU 190 | {EF1FF2CC-BC36-497B-9D21-E0851909325F}.Release|Any CPU.ActiveCfg = Release|Any CPU 191 | {EF1FF2CC-BC36-497B-9D21-E0851909325F}.Release|Any CPU.Build.0 = Release|Any CPU 192 | {72DAEC1C-873C-43BC-B027-9FD67AF7E4B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 193 | {72DAEC1C-873C-43BC-B027-9FD67AF7E4B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 194 | {72DAEC1C-873C-43BC-B027-9FD67AF7E4B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 195 | {72DAEC1C-873C-43BC-B027-9FD67AF7E4B8}.Release|Any CPU.Build.0 = Release|Any CPU 196 | {B2ED16D4-F4CB-4B93-817C-C787213D8A6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 197 | {B2ED16D4-F4CB-4B93-817C-C787213D8A6E}.Debug|Any CPU.Build.0 = Debug|Any CPU 198 | {B2ED16D4-F4CB-4B93-817C-C787213D8A6E}.Release|Any CPU.ActiveCfg = Release|Any CPU 199 | {B2ED16D4-F4CB-4B93-817C-C787213D8A6E}.Release|Any CPU.Build.0 = Release|Any CPU 200 | {5028999F-F0EC-4421-A786-606372EC0B01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 201 | {5028999F-F0EC-4421-A786-606372EC0B01}.Debug|Any CPU.Build.0 = Debug|Any CPU 202 | {5028999F-F0EC-4421-A786-606372EC0B01}.Release|Any CPU.ActiveCfg = Release|Any CPU 203 | {5028999F-F0EC-4421-A786-606372EC0B01}.Release|Any CPU.Build.0 = Release|Any CPU 204 | {E17DA84E-69C1-4EB9-AC24-39A9605259EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 205 | {E17DA84E-69C1-4EB9-AC24-39A9605259EE}.Debug|Any CPU.Build.0 = Debug|Any CPU 206 | {E17DA84E-69C1-4EB9-AC24-39A9605259EE}.Release|Any CPU.ActiveCfg = Release|Any CPU 207 | {E17DA84E-69C1-4EB9-AC24-39A9605259EE}.Release|Any CPU.Build.0 = Release|Any CPU 208 | {0D327170-0673-4A43-8A29-07912377A898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 209 | {0D327170-0673-4A43-8A29-07912377A898}.Debug|Any CPU.Build.0 = Debug|Any CPU 210 | {0D327170-0673-4A43-8A29-07912377A898}.Release|Any CPU.ActiveCfg = Release|Any CPU 211 | {0D327170-0673-4A43-8A29-07912377A898}.Release|Any CPU.Build.0 = Release|Any CPU 212 | {DAE9ABA6-E347-4659-B579-D80A6AC2000E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 213 | {DAE9ABA6-E347-4659-B579-D80A6AC2000E}.Debug|Any CPU.Build.0 = Debug|Any CPU 214 | {DAE9ABA6-E347-4659-B579-D80A6AC2000E}.Release|Any CPU.ActiveCfg = Release|Any CPU 215 | {DAE9ABA6-E347-4659-B579-D80A6AC2000E}.Release|Any CPU.Build.0 = Release|Any CPU 216 | {1E79EA40-B395-4D11-AA12-A9ED08D18983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 217 | {1E79EA40-B395-4D11-AA12-A9ED08D18983}.Debug|Any CPU.Build.0 = Debug|Any CPU 218 | {1E79EA40-B395-4D11-AA12-A9ED08D18983}.Release|Any CPU.ActiveCfg = Release|Any CPU 219 | {1E79EA40-B395-4D11-AA12-A9ED08D18983}.Release|Any CPU.Build.0 = Release|Any CPU 220 | {5D8E595F-6C68-47DE-9D1A-538AA6720A02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 221 | {5D8E595F-6C68-47DE-9D1A-538AA6720A02}.Debug|Any CPU.Build.0 = Debug|Any CPU 222 | {5D8E595F-6C68-47DE-9D1A-538AA6720A02}.Release|Any CPU.ActiveCfg = Release|Any CPU 223 | {5D8E595F-6C68-47DE-9D1A-538AA6720A02}.Release|Any CPU.Build.0 = Release|Any CPU 224 | {E904AF5C-35A2-40F9-8EF2-0FDE5390D6F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 225 | {E904AF5C-35A2-40F9-8EF2-0FDE5390D6F6}.Debug|Any CPU.Build.0 = Debug|Any CPU 226 | {E904AF5C-35A2-40F9-8EF2-0FDE5390D6F6}.Release|Any CPU.ActiveCfg = Release|Any CPU 227 | {E904AF5C-35A2-40F9-8EF2-0FDE5390D6F6}.Release|Any CPU.Build.0 = Release|Any CPU 228 | {F1BF0BC9-38BC-432B-AB28-4BF68A1A344C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 229 | {F1BF0BC9-38BC-432B-AB28-4BF68A1A344C}.Debug|Any CPU.Build.0 = Debug|Any CPU 230 | {F1BF0BC9-38BC-432B-AB28-4BF68A1A344C}.Release|Any CPU.ActiveCfg = Release|Any CPU 231 | {F1BF0BC9-38BC-432B-AB28-4BF68A1A344C}.Release|Any CPU.Build.0 = Release|Any CPU 232 | {FB7E260A-C146-40C3-BF7C-641BEF92A7A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 233 | {FB7E260A-C146-40C3-BF7C-641BEF92A7A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 234 | {FB7E260A-C146-40C3-BF7C-641BEF92A7A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 235 | {FB7E260A-C146-40C3-BF7C-641BEF92A7A0}.Release|Any CPU.Build.0 = Release|Any CPU 236 | EndGlobalSection 237 | GlobalSection(SolutionProperties) = preSolution 238 | HideSolutionNode = FALSE 239 | EndGlobalSection 240 | GlobalSection(NestedProjects) = preSolution 241 | {4A96FD48-1F32-4787-8914-CE61765E2D7B} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 242 | {AB5DD339-5F31-4B97-B883-8D87119A4D9C} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 243 | {37C9ABAA-E217-43C1-9E78-D45B721DA6B7} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 244 | {ADF5C5A0-292A-47E8-933D-0E49159ECF8A} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 245 | {1E5B4E96-249C-4239-81E1-ECC45EC1E84E} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 246 | {B73A83B8-8E52-4CBA-AA33-19073A170AE1} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 247 | {3293F4D5-E1A8-46BA-A1B3-B06095D16FBA} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 248 | {039921D8-4248-4929-9600-F6794AEB46E0} = {A520C293-BEE3-463F-A448-488C5EC15D24} 249 | {2242FC5A-384D-4644-85C0-8D8AA1D7D2DB} = {A520C293-BEE3-463F-A448-488C5EC15D24} 250 | {DCE0ABDE-C706-4357-B2C2-508E5E5E4028} = {A520C293-BEE3-463F-A448-488C5EC15D24} 251 | {F6BCF270-F199-4DD7-A1C3-D4132FDBDC03} = {A520C293-BEE3-463F-A448-488C5EC15D24} 252 | {495F1E24-3A2E-44D1-BE21-07ABAD784B22} = {A520C293-BEE3-463F-A448-488C5EC15D24} 253 | {8B8EEB17-30F9-40D5-9C6B-BB80E0C5E5CC} = {A520C293-BEE3-463F-A448-488C5EC15D24} 254 | {9F7C7EF9-DFE1-4CF1-BBA4-E76618D973E8} = {A520C293-BEE3-463F-A448-488C5EC15D24} 255 | {2B8CA8C4-B09F-4161-9687-14CCC589D1F4} = {A520C293-BEE3-463F-A448-488C5EC15D24} 256 | {39EA2A18-2DD0-4C9D-988F-E61EF77ADF1A} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 257 | {59677058-994E-487E-B294-EE9C71DA96D2} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 258 | {16A1506C-A00D-450F-8782-43060B8C240B} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 259 | {5B2200C9-36FE-4D5B-BA73-B0171FAE6A01} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 260 | {165E63CF-5C74-4DF5-A4B1-5883ED032F2C} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 261 | {80F6E25B-6F22-4D14-9C0D-9C6AEF30A5D1} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 262 | {6D8DC78C-F1D1-43E6-B1FC-3DA5AE1CF8D2} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 263 | {3E5DC4CA-772C-4FAF-8B11-CC3AECE210E0} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 264 | {2A91ABB0-3955-4E08-8C20-6231A2D0F17D} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 265 | {EF1FF2CC-BC36-497B-9D21-E0851909325F} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 266 | {72DAEC1C-873C-43BC-B027-9FD67AF7E4B8} = {7A660546-3B3E-44B8-BF65-422CDA59BED4} 267 | {B2ED16D4-F4CB-4B93-817C-C787213D8A6E} = {7A660546-3B3E-44B8-BF65-422CDA59BED4} 268 | {5028999F-F0EC-4421-A786-606372EC0B01} = {A520C293-BEE3-463F-A448-488C5EC15D24} 269 | {E17DA84E-69C1-4EB9-AC24-39A9605259EE} = {7A660546-3B3E-44B8-BF65-422CDA59BED4} 270 | {0D327170-0673-4A43-8A29-07912377A898} = {66EE5B92-EBBC-4F0A-A67F-232B0F1FA5C0} 271 | {DAE9ABA6-E347-4659-B579-D80A6AC2000E} = {7A660546-3B3E-44B8-BF65-422CDA59BED4} 272 | {1E79EA40-B395-4D11-AA12-A9ED08D18983} = {A520C293-BEE3-463F-A448-488C5EC15D24} 273 | {5D8E595F-6C68-47DE-9D1A-538AA6720A02} = {B5EA3087-36A6-4F1B-B57D-B4A0439552AF} 274 | {E904AF5C-35A2-40F9-8EF2-0FDE5390D6F6} = {A520C293-BEE3-463F-A448-488C5EC15D24} 275 | {F1BF0BC9-38BC-432B-AB28-4BF68A1A344C} = {A520C293-BEE3-463F-A448-488C5EC15D24} 276 | {FB7E260A-C146-40C3-BF7C-641BEF92A7A0} = {A520C293-BEE3-463F-A448-488C5EC15D24} 277 | EndGlobalSection 278 | GlobalSection(ExtensibilityGlobals) = postSolution 279 | SolutionGuid = {A562724F-A53A-4048-8F82-F40597B34CE3} 280 | EndGlobalSection 281 | EndGlobal 282 | --------------------------------------------------------------------------------