Practical.cleanarchitecture

Full-stack .Net 7 Clean Architecture (Microservices, Modular Monolith, Monolith), Blazor, Angular 16, React 18, Vue 3, BFF with YARP, Domain-Driven Design, CQRS, SOLID, Asp.Net Core Identity Custom Storage, OpenID Connect, Entity Framework Core, Selenium, SignalR, Hosted Services, Health Checks, Rate Limiting, Cloud Services (Azure, AWS, Google)...
Alternatives To Practical.cleanarchitecture
Project NameStarsDownloadsRepos Using ThisPackages Using ThisMost Recent CommitTotal ReleasesLatest ReleaseOpen IssuesLicenseLanguage
Devops Exercises59,722
2 days ago53otherPython
Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions
Pulumi18,35713279410 hours ago3,924December 01, 20232,024apache-2.0Go
Pulumi - Infrastructure as Code in any programming language. Build infrastructure intuitively on any cloud using familiar languages 🚀
Awesome Kubernetes14,349
a month ago15otherShell
A curated list for awesome kubernetes sources :ship::tada:
Sops14,2991212 days ago16October 11, 2023373mpl-2.0Go
Simple and flexible tool for managing secrets
Caprover11,313
16 days ago113otherTypeScript
Scalable PaaS (automated Docker+nginx) - aka Heroku on Steroids
Infracost9,754
11 hours ago148October 27, 2023169apache-2.0Go
Cloud cost estimates for Terraform in pull requests💰📉 Love your cloud bill!
Go Cloud9,2538705 days ago99August 25, 202311apache-2.0Go
The Go Cloud Development Kit (Go CDK): A library and tools for open cloud development in Go.
Prowler8,869
11 hours ago55November 16, 202327apache-2.0Python
Prowler is an Open Source Security tool for AWS, Azure and GCP to perform Cloud Security best practices assessments, audits, incident response, compliance, continuous monitoring, hardening and forensics readiness. Includes CIS, NIST 800, NIST CSF, CISA, FedRAMP, PCI-DSS, GDPR, HIPAA, FFIEC, SOC2, GXP, Well-Architected Security, ENS and more.
Metaflow7,19012519 hours ago103December 04, 2023308apache-2.0Python
:rocket: Build and manage real-life data science projects with ease!
Midway6,96927712 hours ago269November 13, 2023139mitTypeScript
🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈
Alternatives To Practical.cleanarchitecture
Select To Compare


Alternative Project Comparisons
Readme

⚠️ Warning

The code samples contain multiple ways and patterns to do things and not always be considered best practices or recommended for all situations.

Database Centric vs Domain Centric Architecture

alt text

(open on draw.io)

Hexagonal Architecture

alt text

(open on draw.io)

Onion Architecture

alt text

(open on draw.io)

The Clean Architecture

alt text

(open on draw.io)

Classic Three-layer Architecture

alt text

(open on draw.io)

Modern Four-layer Architecture

alt text

(open on draw.io)

Layer Dependencies

alt text

(open on draw.io)

Layer Examples

alt text

(open on draw.io)

Testing Pyramid

alt text alt text alt text

(open on draw.io)

Vertical Slice Architecture (Modular Monolith)

alt text

(open on draw.io)

Solution Structure

alt text

alt text

alt text

How to Run:

Update Configuration

Database
  • Update Connection Strings:

    Project Configuration File Configuration Key
    ClassifiedAds.Migrator appsettings.json ConnectionStrings:ClassifiedAds
    ClassifiedAds.BackgroundServer appsettings.json ConnectionStrings:ClassifiedAds
    ClassifiedAds.IdentityServer appsettings.json ConnectionStrings:ClassifiedAds
    ClassifiedAds.WebAPI appsettings.json ConnectionStrings:ClassifiedAds
    ClassifiedAds.WebMVC appsettings.json ConnectionStrings:ClassifiedAds
  • Run Migration:

    • Option 1: Using dotnet cli:
      • Install dotnet-ef cli:
        dotnet tool install --global dotnet-ef --version="5.0"
        
      • Navigate to ClassifiedAds.Migrator and run these commands:
        dotnet ef migrations add Init --context AdsDbContext -o Migrations/AdsDb
        dotnet ef migrations add Init --context ConfigurationDbContext -o Migrations/ConfigurationDb
        dotnet ef migrations add Init --context PersistedGrantDbContext -o Migrations/PersistedGrantDb
        dotnet ef database update --context AdsDbContext
        dotnet ef database update --context ConfigurationDbContext
        dotnet ef database update --context PersistedGrantDbContext
        
    • Option 2: Using Package Manager Console:
      • Set ClassifiedAds.Migrator as StartUp Project
      • Open Package Manager Console, select ClassifiedAds.Migrator as Default Project
      • Run these commands:
        Add-Migration -Context AdsDbContext Init -OutputDir Migrations/AdsDb
        Add-Migration -Context ConfigurationDbContext Init -OutputDir Migrations/ConfigurationDb
        Add-Migration -Context PersistedGrantDbContext Init -OutputDir Migrations/PersistedGrantDb
        Update-Database -Context AdsDbContext
        Update-Database -Context ConfigurationDbContext
        Update-Database -Context PersistedGrantDbContext
        
Additional Configuration Sources
  • Open ClassifiedAds.WebMVC/appsettings.json and jump to ConfigurationSources section.

    "ConfigurationSources": {
      "SqlServer": {
        "IsEnabled": false,
        "ConnectionString": "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#",
        "SqlQuery": "select [Key], [Value] from ConfigurationEntries"
      },
      "AzureKeyVault": {
        "IsEnabled": false,
        "VaultName": "https://xxx.vault.azure.net/"
      }
    },
    
  • Get from Sql Server database:

    "ConfigurationSources": {
      "SqlServer": {
        "IsEnabled": true,
        "ConnectionString": "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#",
        "SqlQuery": "select [Key], [Value] from ConfigurationEntries"
      },
    },
    
  • Get from Azure Key Vault:

    "ConfigurationSources": {
      "AzureKeyVault": {
        "IsEnabled": true,
        "VaultName": "https://xxx.vault.azure.net/"
      }
    },
    
  • Use Both:

    "ConfigurationSources": {
      "SqlServer": {
        "IsEnabled": true,
        "ConnectionString": "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#",
        "SqlQuery": "select [Key], [Value] from ConfigurationEntries"
      },
      "AzureKeyVault": {
        "IsEnabled": true,
        "VaultName": "https://xxx.vault.azure.net/"
      }
    },
    
Storage
  • Open ClassifiedAds.WebMVC/appsettings.json, ClassifiedAds.WebAPI/appsettings.json and jump to Storage section.

    "Storage": {
      "Provider": "Local",
    },
    
  • Use Local Files:

    "Storage": {
      "Provider": "Local",
      "Local": {
        "Path": "E:\\files"
      },
    },
    
  • Use Azure Blob:

    "Storage": {
      "Provider": "Azure",
      "Azure": {
        "ConnectionString": "xxx",
        "Container": "classifiedadds"
      },
    },
    
  • Use Amazon S3:

    "Storage": {
      "Provider": "Amazon",
      "Amazon": {
        "AccessKeyID": "xxx",
        "SecretAccessKey": "xxx",
        "BucketName": "classifiedadds",
        "RegionEndpoint": "ap-southeast-1"
      }
    },
    
Message Broker
  • Open below files and jump to MessageBroker section:

    "MessageBroker": {
      "Provider": "RabbitMQ",
    }
    
  • Use RabbitMQ

    "MessageBroker": {
      "Provider": "RabbitMQ",
      "RabbitMQ": {
        "HostName": "localhost",
        "UserName": "guest",
        "Password": "guest",
        "ExchangeName": "amq.direct",
        "RoutingKeys": {
          "FileUploadedEvent": "classifiedadds_fileuploaded",
          "FileDeletedEvent": "classifiedadds_filedeleted",
          "EmailMessageCreatedEvent": "classifiedadds_emailcreated",
          "SmsMessageCreatedEvent": "classifiedadds_smscreated"
        },
        "QueueNames": {
          "FileUploadedEvent": "classifiedadds_fileuploaded",
          "FileDeletedEvent": "classifiedadds_filedeleted",
          "EmailMessageCreatedEvent": "classifiedadds_emailcreated",
          "SmsMessageCreatedEvent": "classifiedadds_smscreated"
        }
      }
    }
    
  • Use Kafka:

    "MessageBroker": {
      "Provider": "Kafka",
      "Kafka": {
        "BootstrapServers": "localhost:9092",
        "Topics": {
          "FileUploadedEvent": "classifiedadds_fileuploaded",
          "FileDeletedEvent": "classifiedadds_filedeleted",
          "EmailMessageCreatedEvent": "classifiedadds_emailcreated",
          "SmsMessageCreatedEvent": "classifiedadds_smscreated"
        },
      }
    }
    
  • Use Azure Queue Storage:

    "MessageBroker": {
      "Provider": "AzureQueue",
      "AzureQueue": {
        "ConnectionString": "xxx",
        "QueueNames": {
          "FileUploadedEvent": "classifiedadds-fileuploaded",
          "FileDeletedEvent": "classifiedadds-filedeleted",
          "EmailMessageCreatedEvent": "classifiedadds-emailcreated",
          "SmsMessageCreatedEvent": "classifiedadds-smscreated"
        }
      }
    }
    
  • Use Azure Service Bus:

    "MessageBroker": {
      "Provider": "AzureServiceBus",
      "AzureServiceBus": {
        "ConnectionString": "xxx",
        "QueueNames": {
          "FileUploadedEvent": "classifiedadds_fileuploaded",
          "FileDeletedEvent": "classifiedadds_filedeleted",
          "EmailMessageCreatedEvent": "classifiedadds_emailcreated",
          "SmsMessageCreatedEvent": "classifiedadds_smscreated"
        }
      }
    }
    
  • Use Azure Event Grid:

    "MessageBroker": {
      "Provider": "AzureEventGrid",
      "AzureEventGrid": {
        "DomainEndpoint": "https://xxx.xxx-1.eventgrid.azure.net/api/events",
        "DomainKey": "xxxx",
        "Topics": {
          "FileUploadedEvent": "classifiedadds_fileuploaded",
          "FileDeletedEvent": "classifiedadds_filedeleted"
          "EmailMessageCreatedEvent": "classifiedadds_emailcreated",
          "SmsMessageCreatedEvent": "classifiedadds_smscreated"
        }
      }
    }
    
  • Use Azure Event Hubs:

    "MessageBroker": {
      "Provider": "AzureEventHub",
      "AzureEventHub": {
        "ConnectionString": "Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=xxx;SharedAccessKey=xxx",
        "Hubs": {
          "FileUploadedEvent": "classifiedadds_fileuploaded",
          "FileDeletedEvent": "classifiedadds_filedeleted",
          "EmailMessageCreatedEvent": "classifiedadds_emailcreated",
          "SmsMessageCreatedEvent": "classifiedadds_smscreated"
        },
        "StorageConnectionString": "DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=xxx;EndpointSuffix=core.windows.net",
        "StorageContainerNames": {
          "FileUploadedEvent": "eventhub-fileuploaded",
          "FileDeletedEvent": "eventhub-filedeleted",
          "EmailMessageCreatedEvent": "eventhub-emailcreated",
          "SmsMessageCreatedEvent": "eventhub-smscreated"
        }
      }
    }
    
Logging
  • Open and jump to Logging section of below files:
    "Logging": {
      "LogLevel": {
        "Default": "Warning"
      },
      "File": {
        "MinimumLogEventLevel": "Information"
      },
      "Elasticsearch": {
        "IsEnabled": false,
        "Host": "http://localhost:9200",
        "IndexFormat": "classifiedads",
        "MinimumLogEventLevel": "Information"
      },
      "EventLog": {
        "IsEnabled": false,
        "LogName": "Application",
        "SourceName": "ClassifiedAds.WebAPI"
      }
    },
    
  • Write to Local file (./logs/log.txt). Always enabled.
    "Logging": {
      "File": {
        "MinimumLogEventLevel": "Information"
      },
    },
    
  • Write to Elasticsearch:
    "Logging": {
      "Elasticsearch": {
        "IsEnabled": true,
        "Host": "http://localhost:9200",
        "IndexFormat": "classifiedads",
        "MinimumLogEventLevel": "Information"
      },
    },
    
  • Write to Windows Event Log (Windows only):
    "Logging": {
      "EventLog": {
        "IsEnabled": true,
        "LogName": "Application",
        "SourceName": "ClassifiedAds.WebAPI"
      }
    },
    
  • Enable all options:
    "Logging": {
      "LogLevel": {
        "Default": "Warning"
      },	
      "File": {
        "MinimumLogEventLevel": "Information"
      },
      "Elasticsearch": {
        "IsEnabled": true,
        "Host": "http://localhost:9200",
        "IndexFormat": "classifiedads",
        "MinimumLogEventLevel": "Information"
      },
      "EventLog": {
        "IsEnabled": true,
        "LogName": "Application",
        "SourceName": "ClassifiedAds.WebAPI"
      }
    },
    
Caching
  • Open and jump to Caching section of below files:
    "Caching": {
      "InMemory": {
    
      },
      "Distributed": {
    
      }
    },
    
  • Configure options for In Memory Cache:
    "Caching": {
      "InMemory": {
        "SizeLimit": null
      },
    },
    
  • Use In Memory Distributed Cache (For Local Testing):
    "Caching": {
      "Distributed": {
        "Provider": "InMemory",
        "InMemory": {
          "SizeLimit": null
        }
      }
    },
    
  • Use Redis Distributed Cache:
    "Caching": {
      "Distributed": {
        "Provider": "Redis",
        "Redis": {
          "Configuration": "xxx.redis.cache.windows.net:6380,password=xxx,ssl=True,abortConnect=False",
          "InstanceName": ""
        }
      }
    },
    
  • Use Sql Server Distributed Cache:
    dotnet tool install --global dotnet-sql-cache --version="5.0"
    dotnet sql-cache create "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#" dbo CacheEntries
    
    "Caching": {
      "Distributed": {
        "Provider": "SqlServer",
        "SqlServer": {
          "ConnectionString": "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#",
          "SchemaName": "dbo",
          "TableName": "CacheEntries"
        }
      }
    },
    
Monitoring
  • Open and jump to Monitoring section of below files:
    "Monitoring": {
      "MiniProfiler": {
        
      },
      "AzureApplicationInsights": {
        
      }
    },
    
  • Use MiniProfiler:
    "Monitoring": {
      "MiniProfiler": {
        "IsEnabled": true,
        "SqlServerStorage": {
          "ConectionString": "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#;MultipleActiveResultSets=true;Encrypt=False",
          "ProfilersTable": "MiniProfilers",
          "TimingsTable": "MiniProfilerTimings",
          "ClientTimingsTable": "MiniProfilerClientTimings"
        }
      },
    },
    
  • Use Azure Application Insights:
    "Monitoring": {
      "AzureApplicationInsights": {
        "IsEnabled": true,
    	"InstrumentationKey": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    	"EnableSqlCommandTextInstrumentation": true
      }
    },
    
  • Use AppMetrics:
    "Monitoring": {
      "AppMetrics": {
        "IsEnabled": true,
        "MetricsOptions": {
          "DefaultContextLabel": "ClassifiedAds.WebAPI",
          "Enabled": true,
          "ReportingEnabled": true
        },
        "MetricsWebTrackingOptions": {
          "ApdexTrackingEnabled": true,
          "ApdexTSeconds": 0.1,
          "IgnoredHttpStatusCodes": [ 404 ],
          "IgnoredRoutesRegexPatterns": [],
          "OAuth2TrackingEnabled": true
        },
        "MetricEndpointsOptions": {
          "MetricsEndpointEnabled": true,
          "MetricsTextEndpointEnabled": true,
          "EnvironmentInfoEndpointEnabled": true
        }
      }
    },
    
  • Use Both:
    "Monitoring": {
      "MiniProfiler": {
        "IsEnabled": true,
        "SqlServerStorage": {
          "ConectionString": "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#;MultipleActiveResultSets=true;Encrypt=False",
          "ProfilersTable": "MiniProfilers",
          "TimingsTable": "MiniProfilerTimings",
          "ClientTimingsTable": "MiniProfilerClientTimings"
        }
      },
      "AzureApplicationInsights": {
        "IsEnabled": true,
        "InstrumentationKey": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "EnableSqlCommandTextInstrumentation": true
      },
      "AppMetrics": {
        "IsEnabled": true,
        "MetricsOptions": {
          "DefaultContextLabel": "ClassifiedAds.WebAPI",
          "Enabled": true,
          "ReportingEnabled": true
        },
        "MetricsWebTrackingOptions": {
          "ApdexTrackingEnabled": true,
          "ApdexTSeconds": 0.1,
          "IgnoredHttpStatusCodes": [ 404 ],
          "IgnoredRoutesRegexPatterns": [],
          "OAuth2TrackingEnabled": true
        },
        "MetricEndpointsOptions": {
          "MetricsEndpointEnabled": true,
          "MetricsTextEndpointEnabled": true,
          "EnvironmentInfoEndpointEnabled": true
        }
      }
    },
    
Interceptors
Security Headers
  • Open ClassifiedAds.WebAPI/appsettings.json and jump to SecurityHeaders section:
    "SecurityHeaders": {
      "Cache-Control": "no-cache, no-store, must-revalidate",
      "Pragma": "no-cache",
      "Expires": "0"
    },
    
  • Open ClassifiedAds.WebMVC/appsettings.json and jump to SecurityHeaders section:
    "SecurityHeaders": {
      "Content-Security-Policy": "form-action 'self'; frame-ancestors 'none'",
      "Feature-Policy": "camera 'none'",
      "Referrer-Policy": "strict-origin-when-cross-origin",
      "X-Content-Type-Options": "nosniff",
      "X-Frame-Options": "DENY",
      "X-XSS-Protection": "1; mode=block",
      "Cache-Control": "no-cache, no-store, must-revalidate",
      "Pragma": "no-cache",
      "Expires": "0"
    },
    
Cross-Origin Resource Sharing (CORS)
External Login
  • Open ClassifiedAds.IdentityServer/appsettings.json and jump to ExternalLogin section:
    "ExternalLogin": {
      "AzureActiveDirectory": {
        "IsEnabled": true,
        "Authority": "https://login.microsoftonline.com/<Directory (tenant) ID>",
        "ClientId": "<Application (client) ID",
        "ClientSecret": "xxx"
      },
      "Microsoft": {
        "IsEnabled": true,
        "ClientId": "<Application (client) ID",
        "ClientSecret": "xxx"
      },
      "Google": {
        "IsEnabled": true,
        "ClientId": "xxx",
        "ClientSecret": "xxx"
      },
      "Facebook": {
        "IsEnabled": true,
        "AppId": "xxx",
        "AppSecret": "xxx"
      }
    },
    
Sending Email
  • Open ClassifiedAds.BackgroundServer/appsettings.json and jump to Notification -> Email section:
    "Notification": {
      "Email": {
        "Provider": "Fake",
      }
    }
    
  • Use SmtpClient:
    "Notification": {
      "Email": {
        "Provider": "SmtpClient",
        "SmtpClient": {
          "Host": "localhost",
          "Port": "",
          "UserName": "",
          "Password": "",
          "EnableSsl": ""
        }
      }
    }
    
Sending SMS
  • Open ClassifiedAds.BackgroundServer/appsettings.json and jump to Notification -> Sms section:
    "Notification": {
      "Sms": {
        "Provider": "Fake",
      }
    }
    
  • Use Twilio
    "Notification": {
      "Sms": {
        "Provider": "Twilio",
        "Twilio": {
          "AccountSId": "",
          "AuthToken": "",
          "FromNumber": ""
        }
      }
    }
    

Set Startup Projects

alt text

Run or Debug the Solution

How to Build and Run Single Page Applications:

  • Angular:

    • Navigate to folder: UIs/angular/

      npm install
      ng serve
      
    • Update environment.ts & environment.prod.ts

      export const environment = {
        OpenIdConnect: {
          Authority: "https://localhost:44367",
          ClientId: "ClassifiedAds.Angular"
        },
        ResourceServer: {
          Endpoint: "https://localhost:44312/api/"
        },
        CurrentUrl: "http://localhost:4200/"
      };
      
    • Go to http://localhost:4200/

      alt text

  • React:

    • Navigate to folder: UIs/reactjs/

      npm install
      npm run start
      
    • Update environment.dev.js & environment.js

      const environment = {
          OpenIdConnect: {
              Authority: "https://localhost:44367",
              ClientId: "ClassifiedAds.React"
          },
          ResourceServer: {
              Endpoint: "https://localhost:44312/api/"
          },
          CurrentUrl: "http://localhost:3000/"
      };
      export default environment;
      
    • Go to http://localhost:3000/

      alt text

  • Vue:

    • Navigate to folder: UIs/vuejs/
      npm install
      npm run serve
      
    • Update environment.dev.js & environment.dev.js
      const environment = {
          OpenIdConnect: {
              Authority: "https://localhost:44367",
              ClientId: "ClassifiedAds.Vue"
          },
          ResourceServer: {
              Endpoint: "https://localhost:44312/api/"
          },
          CurrentUrl: "http://localhost:8080/"
      };
      export default environment;
      

How to Run on Docker Containers:

How to Run Integration & End to End Tests:

  • Update ClassifiedAds.IntegrationTests/appsettings.json

    {
      "OpenIdConnect": {
        "Authority": "https://localhost:44367",
        "ClientId": "ClassifiedAds.WebMVC",
        "ClientSecret": "secret",
        "RequireHttpsMetadata": "true"
      },
      "WebAPI": {
        "Endpoint": "https://localhost:44312"
      },
      "GraphQL": {
        "Endpoint": "https://localhost:44392/graphql"
      },
      "Login": {
        "UserName": "[email protected]",
        "Password": "v*7Un8b4rcN@<-RN",
        "Scope": "ClassifiedAds.WebAPI"
      }
    }
    
  • Download Chrome Driver

    alt text

  • Update ClassifiedAds.EndToEndTests/appsettings.json

    {
      "ChromeDriverPath": "D:\\Downloads\\chromedriver_win32\\72",
      "Login": {
        "Url": "https://localhost:44364/Home/Login",
        "UserName": "[email protected]",
        "Password": "v*7Un8b4rcN@<-RN"
      }
    }
    

    alt text

Application URLs:

https://github.com/phongnguyend/Practical.CleanArchitecture/wiki/Application-URLs

Roadmap:

https://github.com/phongnguyend/Practical.CleanArchitecture/wiki/Roadmap

Licence 🔑

This repository is licensed under the MIT license.

Duende.IdentityServer License 🔑

Duende.IdentityServer is available under both a FOSS (RPL) and a commercial license.

For the production environment, it is necessary to get a specific license, if you would like more information about the licensing of Duende.IdentityServer - please check this link.

The source code under /src/IdentityServer/Duende folder uses the source code from https://github.com/DuendeSoftware/IdentityServer.Quickstart.UI which is under the terms of the following license.

EPPLus License 🔑

EPPlus 5 can be used under Polyform Noncommercial license or a commercial license.

For the production environment, it is necessary to get a specific license, if you would like more information about the licensing of EPPlus 5 - please check this link.

Popular Amazon Web Services Projects
Popular Azure Projects
Popular Cloud Computing Categories
Related Searches

Get A Weekly Email With Trending Projects For These Categories
No Spam. Unsubscribe easily at any time.
C Sharp
Reactjs
Docker
Vuejs
Angular
Aws
Kubernetes
Azure
Oauth2
Microservice
Kafka
Health
Rabbitmq
Identity
Domain Driven Design
Cqrs
Owasp
Signalr