- Notifications
You must be signed in to change notification settings - Fork585
Full-stack .Net 9 Clean Architecture (Microservices, Modular Monolith, Monolith), Blazor, Angular 20, React 19, Vue 3.5, BFF with YARP, NextJs 15, Domain-Driven Design, CQRS, SOLID, Asp.Net Core Identity Custom Storage, OpenID Connect, EF Core, OpenTelemetry, SignalR, Background Services, Health Checks, Rate Limiting, Clouds (Azure, AWS, GCP), ..
License
phongnguyend/Practical.CleanArchitecture
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
⚠️ WarningThe code samples contain multiple ways and patterns to do things and not always be considered best practices or recommended for all situations.
Additional Configuration Sources
OpenClassifiedAds.WebMVC/appsettings.json and jump toConfigurationSources 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
OpenClassifiedAds.WebMVC/appsettings.json,ClassifiedAds.WebAPI/appsettings.json and jump toStorage 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 toMessaging section:
"Messaging":{"Provider":"RabbitMQ",}
Use RabbitMQ
"Messaging":{"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:
"Messaging":{"Provider":"Kafka","Kafka":{"BootstrapServers":"localhost:9092","Topics":{"FileUploadedEvent":"classifiedadds_fileuploaded","FileDeletedEvent":"classifiedadds_filedeleted","EmailMessageCreatedEvent":"classifiedadds_emailcreated","SmsMessageCreatedEvent":"classifiedadds_smscreated"},}}
Use Azure Queue Storage:
"Messaging":{"Provider":"AzureQueue","AzureQueue":{"ConnectionString":"xxx","QueueNames":{"FileUploadedEvent":"classifiedadds-fileuploaded","FileDeletedEvent":"classifiedadds-filedeleted","EmailMessageCreatedEvent":"classifiedadds-emailcreated","SmsMessageCreatedEvent":"classifiedadds-smscreated"}}}
Use Azure Service Bus:
"Messaging":{"Provider":"AzureServiceBus","AzureServiceBus":{"ConnectionString":"xxx","QueueNames":{"FileUploadedEvent":"classifiedadds_fileuploaded","FileDeletedEvent":"classifiedadds_filedeleted","EmailMessageCreatedEvent":"classifiedadds_emailcreated","SmsMessageCreatedEvent":"classifiedadds_smscreated"}}}
Logging
- Open and jump toLogging section of below files:
- ClassifiedAds.WebAPI/appsettings.json
- ClassifiedAds.WebMVC/appsettings.json
- ClassifiedAds.Background/appsettings.json
"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 toCaching 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:
dotnettoolinstall--globaldotnet-sql-cache--version="5.0"dotnetsql-cachecreate"Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#"dboCacheEntries
"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 toMonitoring section of below files:
"Monitoring":{},
- Use Azure Application Insights:
"Monitoring":{"AzureApplicationInsights":{"IsEnabled":true,"InstrumentationKey":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","EnableSqlCommandTextInstrumentation":true}},
- Use OpenTelemetry:
"Monitoring":{"OpenTelemetry":{"IsEnabled":true,"ServiceName":"ClassifiedAds.WebAPI","TraceEnabled":true,"MetricEnabled":true,"Otlp":{"IsEnabled":false,"Endpoint":"http://localhost:4317"}}},
- Use Both:
"Monitoring":{"AzureApplicationInsights":{"IsEnabled":true,"InstrumentationKey":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","EnableSqlCommandTextInstrumentation":true},"OpenTelemetry":{"IsEnabled":true,"ServiceName":"ClassifiedAds.WebAPI","TraceEnabled":true,"MetricEnabled":true,"Otlp":{"IsEnabled":false,"Endpoint":"http://localhost:4317"}}},
Interceptors
- Open and jump toInterceptors section of below files:
- ClassifiedAds.WebAPI/appsettings.json
- ClassifiedAds.WebMVC/appsettings.json
- ClassifiedAds.Background/appsettings.json
"Interceptors":{"LoggingInterceptor":true,"ErrorCatchingInterceptor":false},
Security Headers
- OpenClassifiedAds.WebAPI/appsettings.json and jump toSecurityHeaders section:
"SecurityHeaders":{"Cache-Control":"no-cache, no-store, must-revalidate","Pragma":"no-cache","Expires":"0"},
- OpenClassifiedAds.WebMVC/appsettings.json and jump toSecurityHeaders 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)
- OpenClassifiedAds.WebAPI/appsettings.json and jump toCORS section:
"CORS":{"AllowAnyOrigin":false,"AllowedOrigins":["http://localhost:4200","http://localhost:3000","http://localhost:8080"]},
External Login
- OpenClassifiedAds.IdentityServer/appsettings.json and jump toExternalLogin 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
- OpenClassifiedAds.Background/appsettings.json and jump toNotification -> Email section:
"Notification":{"Email":{"Provider":"Fake",}}
- Use SmtpClient:
"Notification":{"Email":{"Provider":"SmtpClient","SmtpClient":{"Host":"localhost","Port":"","UserName":"","Password":"","EnableSsl":""}}}
Sending SMS
- OpenClassifiedAds.Background/appsettings.json and jump toNotification -> Sms section:
"Notification":{"Sms":{"Provider":"Fake",}}
- Use Twilio
"Notification":{"Sms":{"Provider":"Twilio","Twilio":{"AccountSId":"","AuthToken":"","FromNumber":""}}}
Web MVC Home Page:https://localhost:44364/
Navigate to Health Checks UIhttps://localhost:44364/healthchecks-ui#/healthchecks and make sure everything is green.
Login on Identity Server:
- Option 1: Use default created account:
- User Name:phong@gmail.com
- Password: v*7Un8b4rcN@<-RN
- Option 2: Register new account athttps://localhost:44367/Account/Register
- Option 1: Use default created account:
Open Blazor Home Page at:https://localhost:44331
Angular:
Navigate to folder:UIs/angular/
npm installng serve
Updateenvironment.ts &environment.prod.ts
exportconstenvironment={OpenIdConnect:{Authority:"https://localhost:44367",ClientId:"ClassifiedAds.Angular"},ResourceServer:{Endpoint:"https://localhost:44312/api/"},CurrentUrl:"http://localhost:4200/"};
React:
Navigate to folder:UIs/reactjs/
npm installnpm run dev
Updateenvironment.dev.tsx &environment.tsx
constenvironment={OpenIdConnect:{Authority:"https://localhost:44367",ClientId:"ClassifiedAds.React"},ResourceServer:{Endpoint:"https://localhost:44312/api/"},CurrentUrl:"http://localhost:3000/"};exportdefaultenvironment;
Vue:
- Navigate to folder:UIs/vuejs/
npm installnpm run dev
- Updateenvironment.dev.ts &environment.dev.ts
constenvironment={OpenIdConnect:{Authority:"https://localhost:44367",ClientId:"ClassifiedAds.Vue"},ResourceServer:{Endpoint:"https://localhost:44312/api/"},CurrentUrl:"http://localhost:8080/"};exportdefaultenvironment;
- Navigate to folder:UIs/vuejs/
Before Login, go to Identity Serverhttps://localhost:44367/Client to make sure application clients have been registered:
Add Migrations if you haven't done on previous steps:
- Installdotnet-ef cli:
dotnet tool install --global dotnet-ef --version="8.0"
- Navigate toClassifiedAds.Migrator and run these commands:
dotnet ef migrations add Init --context AdsDbContext -o Migrations/AdsDb
- Installdotnet-ef cli:
Navigate toMonolith and run:
docker compose builddocker compose up
Open Web MVC Home Page at:http://host.docker.internal:9003
Navigate to Health Checks UIhttp://host.docker.internal:9003/healthchecks-ui#/healthchecks and make sure everything is green.
Login on Identity Server:
- Use default created account:phong@gmail.com / v*7Un8b4rcN@<-RN
- Register new account athttp://host.docker.internal:9000/Account/Register
Open Blazor Home Page at:http://host.docker.internal:9008
UpdateClassifiedAds.IntegrationTests/appsettings.json
{"OpenIdConnect":{"Authority":"https://localhost:44367","ClientId":"ClassifiedAds.WebMVC","ClientSecret":"secret","RequireHttpsMetadata":"true"},"WebAPI":{"Endpoint":"https://localhost:44312"},"Login":{"UserName":"phong@gmail.com","Password":"v*7Un8b4rcN@<-RN","Scope":"ClassifiedAds.WebAPI"}}
DownloadChrome Driver
UpdateEndToEndUiTests/appsettings.json
{"ChromeDriverPath":"D:\\Downloads\\chromedriver_win32\\72","Login":{"Url":"https://localhost:44364/Home/Login","UserName":"phong@gmail.com","Password":"v*7Un8b4rcN@<-RN"}}
https://github.com/phongnguyend/Practical.CleanArchitecture/wiki/Application-URLs
https://github.com/phongnguyend/Practical.CleanArchitecture/wiki/Roadmap
This repository is licensed under theMIT license.
Duende.IdentityServer is available under both aFOSS (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 ofDuende.IdentityServer - please checkthis link.
The source code under/src/IdentityServers/Duende folder uses the source code fromhttps://github.com/DuendeSoftware/IdentityServer.Quickstart.UI which is under the terms of the followinglicense.
About
Full-stack .Net 9 Clean Architecture (Microservices, Modular Monolith, Monolith), Blazor, Angular 20, React 19, Vue 3.5, BFF with YARP, NextJs 15, Domain-Driven Design, CQRS, SOLID, Asp.Net Core Identity Custom Storage, OpenID Connect, EF Core, OpenTelemetry, SignalR, Background Services, Health Checks, Rate Limiting, Clouds (Azure, AWS, GCP), ..
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Contributors8
Uh oh!
There was an error while loading.Please reload this page.