- •Table of Contents
- •About the Author
- •Acknowledgments
- •Introduction
- •Version Support
- •Supported Versions
- •A Unified Platform
- •Roadmap
- •Supported Operating Systems
- •Command Line Interface
- •Desktop Development
- •Blazor
- •MAUI
- •Wrapping Up
- •.NET 6 Architecture
- •Runtimes
- •CoreCLR
- •Mono
- •WinRT
- •Managed Execution Process
- •Desktop Packs
- •Wrapping Up
- •Dotnet New
- •Dotnet Restore
- •NuGet.config
- •Dotnet Build
- •Dotnet Publish
- •Dotnet Run
- •Dotnet Test
- •Using the CLI in GitHub Actions
- •Other Commands
- •Wrapping Up
- •WinAPI
- •WinForms
- •STAThread
- •WinForms Startup
- •DPI Mode
- •Responding to Scale Events
- •Visual Styles
- •Text Rendering
- •The Message Loop
- •The Form Designer
- •WPF Startup
- •XAML Layout
- •Visual Tree
- •Data Binding
- •Windows App SDK
- •Building a Windows App SDK application
- •Using Windows APIs with Windows App SDK
- •Packaging
- •Migrating to .NET 6
- •Upgrade Assistant
- •Wrapping Up
- •Blazor WebAssembly
- •Creating a Blazor Wasm Project
- •Blazor Progressive Web Apps
- •Exploring the Blazor Client Project
- •Blazor in .NET 6
- •Blazor Component System
- •Creating Blazor Pages
- •Running a Blazor App
- •Blazor Server
- •SignalR
- •Blazor Desktop
- •Wrapping Up
- •Project Structure
- •Exploring MAUI
- •The Cross-Platform World
- •Application Lifecycle
- •MVVM
- •MVVM Toolkit
- •Wrapping Up
- •Model-View-Controller
- •Routing
- •Views
- •Controllers
- •Controller-Based APIs
- •Minimal APIs
- •Wrapping Up
- •Web Apps
- •Creating an App Service
- •Static Web Apps
- •Web App for Containers
- •Docker
- •Azure Functions
- •Deploying Azure Functions
- •Wrapping Up
- •Record Types
- •Monolith Architecture
- •Microservices
- •Container Orchestration
- •Kubernetes
- •Docker Compose
- •Dapr
- •Installing Dapr
- •Dapr State Management
- •Wrapping Up
- •Roslyn
- •Compiler API
- •Diagnostic API
- •Scripting API
- •Workspace API
- •Syntax Tree
- •Roslyn SDK
- •Source Generators
- •Writing a Source Generator
- •Debugging Source Generators
- •Wrapping Up
- •Garbage Collector
- •The Heap
- •The Stack
- •Garbage Collection
- •A Look at the Threadpool
- •Async in .NET 6
- •Await/Async
- •Cancellations
- •WaitAsync
- •Conclusion
- •Index
Chapter 9 Application Architecture
Microservices work great in a Domain-Driven-Design (DDD) or Clean Architecture (CA) scenario. The scope of a microservice can, in most cases, map to a bounded context. Domain-Driven-Design and Clean Architecture are widely popular design patterns for enterprise applications. They both give the domain model responsibility for changes and nicely decouple read and write requests. Both are really great patterns to add to your arsenal as a developer.
A bounded context is a functional block of your application that can be isolated. For example, the orders of a webshop can contain products, customers, purchases, and so on. That isolated block of orders functionality can be a bounded context. However, just like with Microservices, DDD and CA have their place in larger applications. Don’t overengineer; use the right tool for the job instead of using a sledgehammer to drive a nail in a wooden board.
If you are interested in learning more about Clean Architecture or Domain-Driven- Design, I can advise you to take a look at the e-book of eshop on containers or the
Practical Event-Driven Microservices Architecture book available from Apress.
Container Orchestration
We have talked about containers, specifically Docker-based containers, in the ASP.NET chapter. Containers and Microservices are a great match, if there is an orchestrator. A container orchestrator is a tool that manages a set of different container images and how they relate to each other. Can they communicate? Over what port? Which containers get exposed outside of the cluster? And so on. The most common orchestrators are Kubernetes and Docker Compose.
Kubernetes
Kubernetes, or k8s for short (https://kubernetes.io), is a container orchestrator. It can automatically deploy and scale your containerized applications. A set of containers deployed on a Kubernetes instance is called a cluster. To explore the capabilities of Kubernetes, I can advise you to install Minikube via https://minikube.sigs.k8s.io/. Minikube is a local Kubernetes cluster installation that you can use for development. It is available for Windows, Linux, and Mac OS. The installer and install instructions can be downloaded at https://minikube.sigs.k8s.io/docs/start/.
265
Chapter 9 Application Architecture
Figure 9-3. Running Minikube on WSL2
Once Minikube is installed, we can use the Kubernetes CLI through the kubectl command.
Figure 9-4. Kubernetes CLI
Time for some terminology. Kubernetes is a cluster consisting of Nodes. Nodes are actual machines, virtual or physical servers, that have Kubernetes installed and are added to the cluster. Running kubectl gets nodes list the available nodes in the cluster; a local installation of Minikube is a cluster with one node.
Figure 9-5. Nodes in a Minikube cluster
One of the nodes is the control plane: the node that controls the cluster. Communication to and from the control plane happens over the Kubernetes API.
A deployed container on a node is called a Pod. For this example, we will create a Pod from one of the services in eShop On Containers. eShop On Containers is an open source reference architecture by Microsoft; it can be found at https://github.com/ dotnet-architecture/eShopOnContainers. The reason we are using this as an example is because the eShop is a container-ready Microservices architecture. It fits quite right with the topic we are dealing with at the moment.
266
Chapter 9 Application Architecture
Time to create a Pod. Listing 9-7 shows the command to create a deployment on our local Kubernetes cluster.
Listing 9-7. Creating a new deployment to Kubernetes
kubectl create deployment apresseshop --image=eshop/catalog.api
The deployment, and the pod, gets created. The image will start pulling in the background and the container will spin up when ready. To check the status of the nodes, we can use kubectl get pods.
Figure 9-6. 1 Pod running on local cluster
Of course, this is a very basic example and complete overkill of what Kubernetes is intended for. As a more elaborate example, I have deployed the entire eShop On Container example on my local cluster.
Figure 9-7. Deploying a larger Kubernetes cluster
267
Chapter 9 Application Architecture
I didn’t have to manually create each container that would defeat the purpose of a container orchestrator. Instead, the project contains yaml files that Kubernetes can use to deploy and configure a set of services.
Listing 9-8. An example Kubernetes file
apiVersion: apps/v1 kind: Deployment metadata:
name: catalog labels:
app: catalogApi spec:
replicas: 1 selector:
matchLabels: app: catalog
template:
metadata:
labels:
app: catalog spec:
containers:
-name: catalog imagePullPolicy: IfNotPresent image: eshop/catalog.api
Listing 9-8 shows an example of a Kubernetes file that spins up a pod of the catalog API.
Docker Compose
Docker Compose is a popular alternative to Kubernetes. It is more designed to work on a single node, while Kubernetes really shines in big enterprise, multi-server environments. This also means that the learning curve for Docker Compose is much smaller. Using Docker Compose is simple: make sure your applications have their own Docker file, create a docker-compose.yml, and run the up command on the Docker Compose CLI.
268
Chapter 9 Application Architecture
We should already have Docker installed since we have installed Docker Desktop in the previous chapter. Docker is packaged together with Docker Desktop on Windows. On Linux it can be installed through Python’s package manager PIP or by downloading the binary from GitHub. Detailed instructions can be found in the Docker Compose documentation https://docs.docker.com/compose/install/. Listing 9-9 shows a simple example of a Docker Compose file using two of the eshop images.
Listing 9-9. Example of Docker Compose file
version: "3" services:
catalogapi:
container_name: catalogApi image: eshop/catalog.api restart: unless-stopped
webmvc:
container_name: webmvc image: eshop/webmvc restart: unless-stopped
To run this, we execute docker-compose up in a command line window.
Figure 9-8. Running two containers in Docker Compose
269