Practical.CleanArchitecture
Full-stack .Net 10 Clean Architecture (Microservices, Modular Monolith, Monolith), Blazor, Angular 21, React 19, Vue 3.5, BFF with YARP, NextJs 16, 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), ...
Install / Use
/learn @phongnguyend/Practical.CleanArchitectureREADME
:warning: 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

Hexagonal Architecture

Onion Architecture

The Clean Architecture

Classic Three-layer Architecture

Modern Four-layer Architecture

Layer Dependencies

Layer Examples

Testing Pyramid

Vertical Slice Architecture (Modular Monolith)

Solution Structure



How to Run:
Update Configuration
<details> <summary><b>Additional Configuration Sources</b></summary>-
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/" } },
-
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" } },
-
Open below files and jump to Messaging 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" } } }
- Open and jump to Logging 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": { "Min
Related Skills
node-connect
329.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
prose
329.0kOpenProse VM skill pack. Activate on any `prose` command, .prose files, or OpenProse mentions; orchestrates multi-agent workflows.
claude-opus-4-5-migration
81.1kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
frontend-design
81.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
