Kick-start the creation of production-ready multi-tenant SaaS solutions with enterprise-grade security 🚀
PlatformPlatform aims to showcase an end-to-end solution for building a multi-tenant application using Azure, .NET, React, Infrastructure as Code, GitHub workflows, and more. The roadmap includes features such as Single Sign-On (SSO), subscription management, usage tracking, feature flags, A/B testing, rate limiting, multi-region, disaster recovery, localization, accessibility, and much more. Follow the continuously updated roadmap here.
Just getting off the ground, your star can help lift this higher! ⭐ Thanks!
The project is using the newly introduced .NET Aspire to give a "F5 experience". If you have the prerequisites installed, just open the solution in Rider or Visual Studio code and run the AppHost project
. This is how it looks:
The backend is built using the most popular, mature, and commonly used technologies in the .NET ecosystem:
- .NET 8 and C# 12
- .NET Aspire
- ASP.NET Minimal API
- Entity Framework
- MediatR
- FluentValidation
- Mapster
- XUnit, FluentAssertions, NSubstitute, and Bogus
- SonarCloud and JetBrains Code style and Cleanup
Read more about the backend architecture
- Clean Architecture: The codebase is organized into layers that promote separation of concerns and maintainability.
- Domain-Driven Design (DDD): DDD principles are applied to ensure a clear and expressive domain model.
- Command Query Responsibility Segregation (CQRS): This clearly separates read (query) and write (command) operations, adhering to the single responsibility principle (each action is in a separate command).
- Screaming architecture: The architecture is designed with namespaces (folders) per feature, making the concepts easily visible and expressive, rather than organizing the code by types like models and repositories.
- MediatR pipelines: MediatR pipeline behaviors are used to ensure consistent handling of cross-cutting concerns like validation, unit of work, and handling of domain events.
- Strongly Typed IDs: The codebase uses strongly typed IDs, which are a combination of the entity type and the entity ID. This is even at the outer API layer, and Swagger translates this to the underlying contract. This ensures type safety and consistency across the codebase.
- JetBrains Code style and Cleanup: JetBrains Rider/ReSharper is used for code style and automatic cleanup (configured in
.editorconfig
), ensuring consistent code formatting. No need to discuss tabs vs. spaces anymore; Invalid formatting breaks the build. - Monolith prepared for self-contained systems: The codebase is organized into a monolith, but the architecture is prepared for splitting in to self-contained systems. A self-contained system is a large microservice (or a small monolith) that contains the full stack including frontend, background jobs, etc. These can be developed, tested, deployed, and scaled in isolation, making it a good compromise between a large monolith and many small microservices. Unlike the popular backend-for-frontend (BFF) style with one shared frontend, this allows teams to work fully independently.
- Shared Kernel: The codebase uses a shared kernel for all the boilerplate code required to build a clean codebase. The shared kernel ensures consistency between self-contained systems, e.g., enforcing tenant isolation, auditing, tracking, implementation of tactical DDD patterns like aggregate, entities, repository base, ID generation, etc.
Although some features like authentication and multi-tenancy are not yet implemented, the current implementation serves as a solid foundation for building business logic without unnecessary boilerplate.
The frontend is built with these technologies:
PlatformPlatform's cloud infrastructure is built using the latest Azure Platform as a Service (PaaS) technologies:
- Azure Container Apps
- Azure SQL
- Azure Blob Storage
- Azure Service Bus
- Azure Key Vault
- Azure Application Insights
- Azure Log Analytics
- Azure Virtual Network
- Azure Managed Identities
- Azure Container Registry
- Microsoft Defender for Cloud
Read more about this enterprise-grade architecture
- Platform as a Service (PaaS) technologies: Azure is the leading Cloud Service Provider (CSP) when it comes to PaaS technologies. PlatformPlatform uses PaaS technologies which are fully managed by Microsoft, as opposed to Infrastructure as a Service (IaaS) technologies where the customer is responsible for the underlying infrastructure. This means that Microsoft is responsible for the availability of the infrastructure, and you are only responsible for the application and data. This makes it possible for even a small team to run a highly scalable, stable, and secure solution.
- Enterprise-grade security with zero secrets:
- Managed Identities: No secrets are used when Container Apps connect to e.g. Databases, Blob Storage, and Service Bus. The infrastructure uses Managed Identities for all communication with Azure resources, eliminating the need for secrets.
- Federated credentials: Deployment from GitHub to Azure is done using federated credentials, establishing a trust between the GitHub repository and Azure subscription based on the repository's URL, without the need for secrets.
- No secrets expires: Since no secrets are used, there is no need to rotate secrets, and no risk of secrets expiring.
- 100% Security Score: The current infrastructure configuration follows best practices, and the current setup code achieves a 100% Security Score in Microsoft Defender for Cloud. This minimizes the attack surface and protects against even sophisticated attacks.
- Automatic certificate management: The infrastructure is configured to automatically request and renew SSL certificates from Let's Encrypt, eliminating the need for manual certificate management.
- Multiple environments: The setup includes different environments like Development, Staging, and Production, deployed into clearly named resource groups within a single Azure Subscription.
- Multi-region: Spinning up a cluster in a new region is a matter of adding one extra deployment job to the GitHub workflow. This allows customers to select a region where their data is close to the user and local data protection laws like GDPR, CCPA, etc. are followed.
- Azure Container Apps: The application is hosted using Azure Container Apps, which is a new service from Azure that provides a fully managed Kubernetes environment for running containerized applications. You don't need to be a Kubernetes expert to run your application in a scalable and secure environment.
- Scaling from zero to millions of users: The Azure Container App Environment is configured to scale from zero to millions of users, and the infrastructure is configured to scale automatically based on load. This means the starting costs are very low, and the solution can scale to millions of users without any manual intervention. This enables having Development and Staging environments running with very low costs.
- Azure SQL: The database is hosted using Azure SQL Database, which is a fully managed SQL Server instance. SQL Server is known for its high performance, stability, scalability, and security. The server will easily handle millions of users with single-digit millisecond response times.
PlatformPlatform is built on a solid foundation for a modern software development lifecycle (SDLC):
- GitHub Pull Requests
- GitHub Actions
- GitHub projects
- GitHub environments
- GitHub CODEOWNERS
- GitHub dependabot
- Bash scripts
- Bicep
This is how it looks when GitHub workflows has deployed Azure Infrastructure:
These are the resource groups created when deploying one staging cluster, and two production clusters:
This is the security score after deploying PlatformPlatform resources to Azure. Achieving a 100% security score in Azure Defender for Cloud without exemptions is not trivial.
PlatformPlatform is designed to support development on both Mac and Windows. The only requirements are:
- .NET
- .NET Aspire (run
dotnet workload install aspire
) - Docker Desktop
- JetBrains Rider or Visual Studio with JetBrains ReSharper
Want to use Visual Studio Code?
While Visual Studio Code can be used and ReSharper is not strictly necessary, in both cases, you should either install the free ReSharper command line tool to ensure the codebase is formatted correctly.
To install ReSharper command line tool run this command
dotnet tool install -g JetBrains.ReSharper.GlobalTools # Add --arch arm64 for Apple silicon
Having a strong conventions for naming, formatting, code style from the start is saving time and giving high quality code.
To inspect code run this command (Use Solution-Wide Analysis in Rider/ReSharper)
jb inspectcode PlatformPlatform.sln --build --output=result.xml
To clean up code this command (equvelent to Ctrl+E+C
/ Cmd+E+C
in Rider/ReSharper)
jb cleanupcode PlatformPlatform.sln
To debug the system locally we run SQL Server using Docker. The SQL Server password used is stored in an environment variable, and added to the connectionstring by .NET (when running in Azure we use Managed Identities instead of passwords).
Likewise, when running .NET self-contained systems on localhost
we need a valid and trusted certificate, and we need the password when building the Docker image.
Please run the following scripts once to setup your localhost.
-
macOS:
# Generate SQL_SERVER_PASSWORD and add to environment variables SQL_SERVER_PASSWORD="$(LC_ALL=C tr -dc 'a-zA-Z0-9!_#$&%' < /dev/urandom | head -c 16)" echo "export SQL_SERVER_PASSWORD='$SQL_SERVER_PASSWORD'" >> ~/.zshrc # or ~/.bashrc, ~/.bash_profile # Generate CERTIFICATE_PASSWORD and add to environment variables CERTIFICATE_PASSWORD="$(LC_ALL=C tr -dc 'a-zA-Z0-9!_#$&%' < /dev/urandom | head -c 16)" echo "export CERTIFICATE_PASSWORD='$CERTIFICATE_PASSWORD'" >> ~/.zshrc # or ~/.bashrc, ~/.bash_profile # Generate dev certificate dotnet dev-certs https --trust -ep ${HOME}/.aspnet/https/localhost.pfx -p $CERTIFICATE_PASSWORD
-
Windows (not tested):
# Add SQL_SERVER_PASSWORD and add to environment variables $SQL_SERVER_PASSWORD = "<YourSecretPassword>" [Environment]::SetEnvironmentVariable('SQL_SERVER_PASSWORD', $SQL_SERVER_PASSWORD, [System.EnvironmentVariableTarget]::User) # Add CERTIFICATE_PASSWORD and add to environment variables $CERTIFICATE_PASSWORD = "<AnotherSecretPassword>" [Environment]::SetEnvironmentVariable('CERTIFICATE_PASSWORD', $CERTIFICATE_PASSWORD, [System.EnvironmentVariableTarget]::User) # Generate dev certificate dotnet dev-certs https --trust -ep $env:USERPROFILE\.aspnet\https\localhost.pfx -p $env:CERTIFICATE_PASSWORD
To test the system you can now run this (the first time the API runs it will create the database and schema):
# Run from the application folder
cd ./application
dotnet publish ./account-management/Api/Api.csproj --output ./account-management/Api/publish
docker compose up -d
open https://localhost:8443
While developing you might want to only run the SQL Server, and compile and debug the source code from Rider or Visual Studio:
# Run from the application folder
cd ./application
docker compose up sql-server -d
Refer to the Set up automatic deployment of Azure infrastructure and code from GitHub in the cloud-infrastructure/README. It's just one Bash script that you run locally, and it will set up everything for you. 🎉