SlideShare a Scribd company logo
Net Microservices Architecture For Containerized
Net Applications V60 Updated To Aspnet Core 60
Cesar De La Torre download
https://ebookbell.com/product/net-microservices-architecture-for-
containerized-net-applications-v60-updated-to-aspnet-
core-60-cesar-de-la-torre-44435496
Explore and download more ebooks at ebookbell.com
Here are some recommended products that we believe you will be
interested in. You can click the link to download.
Net Microservices Architecture For Containerized Net Applications V70
Updated To Aspnet Core 70 Cesar De La Torre
https://ebookbell.com/product/net-microservices-architecture-for-
containerized-net-applications-v70-updated-to-aspnet-core-70-cesar-de-
la-torre-54560584
Net Microservices Architecture For Containerized Net Applications V10
Cesar De La Torre
https://ebookbell.com/product/net-microservices-architecture-for-
containerized-net-applications-v10-cesar-de-la-torre-5641962
Software Architecture With C 10 And Net 6 Develop Software Solutions
Using Microservices Devops Ef Core And Design Patterns For Azure 3rd
Edition 3rd Gabriel Baptista
https://ebookbell.com/product/software-architecture-with-c-10-and-
net-6-develop-software-solutions-using-microservices-devops-ef-core-
and-design-patterns-for-azure-3rd-edition-3rd-gabriel-
baptista-47994386
Software Architecture With C 9 And Net 5 Architecting Software
Solutions Using Microservices Devops And Design Patterns For Azure
Second Edition 2nd Edition Gabriel Baptista
https://ebookbell.com/product/software-architecture-with-c-9-and-
net-5-architecting-software-solutions-using-microservices-devops-and-
design-patterns-for-azure-second-edition-2nd-edition-gabriel-
baptista-50847388
Software Architecture With C 12 And Net 8 Build Enterprise
Applications Using Microservices Devops Ef Core And Design Patterns
For Azure 4th Edition
https://ebookbell.com/product/software-architecture-with-c-12-and-
net-8-build-enterprise-applications-using-microservices-devops-ef-
core-and-design-patterns-for-azure-4th-edition-59103730
Handson Software Architecture With C 8 And Net Core 3 Architecting
Software Solutions Using Microservices Devops And Design Patterns For
Azure Cloud 1st Edition Gabriel Baptista
https://ebookbell.com/product/handson-software-architecture-
with-c-8-and-net-core-3-architecting-software-solutions-using-
microservices-devops-and-design-patterns-for-azure-cloud-1st-edition-
gabriel-baptista-50851904
Handson Software Architecture With C 8 And Net Core 3 Architecting
Software Solutions Using Microservices Devops And Design Patterns For
Azure Cloud Gabriel Baptista Francesco Abbruzzese
https://ebookbell.com/product/handson-software-architecture-
with-c-8-and-net-core-3-architecting-software-solutions-using-
microservices-devops-and-design-patterns-for-azure-cloud-gabriel-
baptista-francesco-abbruzzese-11074032
Microservices By Examples Using Net Core A Book With Lot Of Practical
And Architectural Styles For Microservices Using Net Core Biswa
Pujarini Mohapatra
https://ebookbell.com/product/microservices-by-examples-using-net-
core-a-book-with-lot-of-practical-and-architectural-styles-for-
microservices-using-net-core-biswa-pujarini-mohapatra-27587818
Implementing Eventdriven Microservices Architecture In Net 7 Develop
Eventbased Distributed Apps That Can Scale With Everchanging Business
Demands Using C 11 And Net 7 1st Edition Joshua Garverick
https://ebookbell.com/product/implementing-eventdriven-microservices-
architecture-in-net-7-develop-eventbased-distributed-apps-that-can-
scale-with-everchanging-business-demands-using-c-11-and-net-7-1st-
edition-joshua-garverick-48224172
Net Microservices Architecture For Containerized Net Applications V60 Updated To Aspnet Core 60 Cesar De La Torre
Net Microservices Architecture For Containerized Net Applications V60 Updated To Aspnet Core 60 Cesar De La Torre
EDITION v6.0 - Updated to ASP.NET Core 6.0
Refer changelog for the book updates and community contributions.
This guide is an introduction to developing microservices-based applications and managing them
using containers. It discusses architectural design and implementation approaches using .NET and
Docker containers.
To make it easier to get started, the guide focuses on a reference containerized and microservice-
based application that you can explore. The reference application is available at the
eShopOnContainers GitHub repo.
Action links
• This e-book is also available in a PDF format (English version only) Download
• Clone/Fork the reference application eShopOnContainers on GitHub
• Watch the introductory video
• Get to know the Microservices Architecture right away
Introduction
Enterprises are increasingly realizing cost savings, solving deployment problems, and improving
DevOps and production operations by using containers. Microsoft has been releasing container
innovations for Windows and Linux by creating products like Azure Kubernetes Service and Azure
Service Fabric, and by partnering with industry leaders like Docker, Mesosphere, and Kubernetes.
These products deliver container solutions that help companies build and deploy applications at cloud
speed and scale, whatever their choice of platform or tools.
Docker is becoming the de facto standard in the container industry, supported by the most significant
vendors in the Windows and Linux ecosystems. (Microsoft is one of the main cloud vendors
supporting Docker.) In the future, Docker will probably be ubiquitous in any datacenter in the cloud or
on-premises.
In addition, the microservices architecture is emerging as an important approach for distributed
mission-critical applications. In a microservice-based architecture, the application is built on a
collection of services that can be developed, tested, deployed, and versioned independently.
About this guide
This guide is an introduction to developing microservices-based applications and managing them
using containers. It discusses architectural design and implementation approaches using .NET and
Docker containers. To make it easier to get started with containers and microservices, the guide
focuses on a reference containerized and microservice-based application that you can explore. The
sample application is available at the eShopOnContainers GitHub repo.
This guide provides foundational development and architectural guidance primarily at a development
environment level with a focus on two technologies: Docker and .NET. Our intention is that you read
this guide when thinking about your application design without focusing on the infrastructure (cloud
or on-premises) of your production environment. You will make decisions about your infrastructure
later, when you create your production-ready applications. Therefore, this guide is intended to be
infrastructure agnostic and more development-environment-centric.
After you have studied this guide, your next step would be to learn about production-ready
microservices on Microsoft Azure.
Version
This guide has been revised to cover .NET 6 version along with many additional updates related to
the same “wave” of technologies (that is, Azure and additional third-party technologies) coinciding in
time with the .NET 6 release. That’s why the book version has also been updated to version 6.0.
What this guide does not cover
This guide does not focus on the application lifecycle, DevOps, CI/CD pipelines, or team work. The
complementary guide Containerized Docker Application Lifecycle with Microsoft Platform and Tools
focuses on that subject. The current guide also does not provide implementation details on Azure
infrastructure, such as information on specific orchestrators.
Additional resources
• Containerized Docker Application Lifecycle with Microsoft Platform and Tools
(downloadable e-book)
https://aka.ms/dockerlifecycleebook
Who should use this guide
We wrote this guide for developers and solution architects who are new to Docker-based application
development and to microservices-based architecture. This guide is for you if you want to learn how
to architect, design, and implement proof-of-concept applications with Microsoft development
technologies (with special focus on .NET) and with Docker containers.
You will also find this guide useful if you are a technical decision maker, such as an enterprise
architect, who wants an architecture and technology overview before you decide on what approach to
select for new and modern distributed applications.
How to use this guide
The first part of this guide introduces Docker containers, discusses how to choose between .NET 6 and
the .NET Framework as a development framework, and provides an overview of microservices. This
content is for architects and technical decision makers who want an overview but don’t need to focus
on code implementation details.
The second part of the guide starts with the Development process for Docker based applications
section. It focuses on the development and microservice patterns for implementing applications using
.NET and Docker. This section will be of most interest to developers and architects who want to focus
on code and on patterns and implementation details.
Related microservice and container-based reference
application: eShopOnContainers
The eShopOnContainers application is an open-source reference app for .NET and microservices that
is designed to be deployed using Docker containers. The application consists of multiple subsystems,
including several e-store UI front-ends (a Web MVC app, a Web SPA, and a native mobile app). It also
includes the back-end microservices and containers for all required server-side operations.
The purpose of the application is to showcase architectural patterns. IT IS NOT A PRODUCTION-
READY TEMPLATE to start real-world applications. In fact, the application is in a permanent beta
state, as it’s also used to test new potentially interesting technologies as they show up.
Send us your feedback!
We wrote this guide to help you understand the architecture of containerized applications and
microservices in .NET. The guide and related reference application will be evolving, so we welcome
your feedback! If you have comments about how this guide can be improved, submit feedback at
https://aka.ms/ebookfeedback.
Credits
Co-Authors:
Cesar de la Torre, Sr. PM, .NET product team, Microsoft Corp.
Bill Wagner, Sr. Content Developer, C+E, Microsoft Corp.
Mike Rousos, Principal Software Engineer, DevDiv CAT team, Microsoft
Editors:
Mike Pope
Steve Hoag
Participants and reviewers:
Jeffrey Richter, Partner Software Eng, Azure team, Microsoft
Jimmy Bogard, Chief Architect at Headspring
Udi Dahan, Founder & CEO, Particular Software
Jimmy Nilsson, Co-founder and CEO of Factor10
Glenn Condron, Sr. Program Manager, ASP.NET team
Mark Fussell, Principal PM Lead, Azure Service Fabric team, Microsoft
Diego Vega, PM Lead, Entity Framework team, Microsoft
Barry Dorrans, Sr. Security Program Manager
Rowan Miller, Sr. Program Manager, Microsoft
Ankit Asthana, Principal PM Manager, .NET team, Microsoft
Scott Hunter, Partner Director PM, .NET team, Microsoft
Nish Anil, Sr. Program Manager, .NET team, Microsoft
Dylan Reisenberger, Architect and Dev Lead at Polly
Steve “ardalis” Smith - Software Architect and Trainer - Ardalis.com
Ian Cooper, Coding Architect at Brighter
Unai Zorrilla, Architect and Dev Lead at Plain Concepts
Eduard Tomas, Dev Lead at Plain Concepts
Ramon Tomas, Developer at Plain Concepts
David Sanz, Developer at Plain Concepts
Javier Valero, Chief Operating Officer at Grupo Solutio
Pierre Millet, Sr. Consultant, Microsoft
Michael Friis, Product Manager, Docker Inc
Charles Lowell, Software Engineer, VS CAT team, Microsoft
Miguel Veloso, Software Development Engineer at Plain Concepts
Sumit Ghosh, Principal Consultant at Neudesic
Copyright
PUBLISHED BY
Microsoft Developer Division, .NET and Visual Studio product teams
A division of Microsoft Corporation
One Microsoft Way
Redmond, Washington 98052-6399
Copyright © 2022 by Microsoft Corporation
All rights reserved. No part of the contents of this book may be reproduced or transmitted in any
form or by any means without the written permission of the publisher.
This book is provided “as-is” and expresses the author’s views and opinions. The views, opinions and
information expressed in this book, including URL and other Internet website references, may change
without notice.
Some examples depicted herein are provided for illustration only and are fictitious. No real association
or connection is intended or should be inferred.
Microsoft and the trademarks listed at https://www.microsoft.com on the “Trademarks” webpage are
trademarks of the Microsoft group of companies.
Mac and macOS are trademarks of Apple Inc.
The Docker whale logo is a registered trademark of Docker, Inc. Used by permission.
All other marks and logos are property of their respective owners.
i Contents
Contents
Introduction to Containers and Docker ................................................................................ 1
What is Docker?........................................................................................................................................................................2
Comparing Docker containers with virtual machines...........................................................................................3
A simple analogy.................................................................................................................................................................4
Docker terminology................................................................................................................................................................5
Docker containers, images, and registries .....................................................................................................................7
Choosing Between .NET 6 and .NET Framework for Docker Containers........................... 9
General guidance.....................................................................................................................................................................9
When to choose .NET for Docker containers............................................................................................................. 10
Developing and deploying cross platform ............................................................................................................ 10
Using containers for new (“green-field”) projects............................................................................................... 11
Create and deploy microservices on containers.................................................................................................. 11
Deploying high density in scalable systems.......................................................................................................... 11
When to choose .NET Framework for Docker containers..................................................................................... 12
Migrating existing applications directly to a Windows Server container .................................................. 12
Using third-party .NET libraries or NuGet packages not available for .NET 6 ......................................... 12
Using .NET technologies not available for .NET 6............................................................................................... 12
Using a platform or API that doesn’t support .NET 6........................................................................................ 13
Porting existing ASP.NET application to .NET 6................................................................................................... 13
Decision table: .NET frameworks to use for Docker................................................................................................ 13
What OS to target with .NET containers...................................................................................................................... 14
Official .NET Docker images ............................................................................................................................................. 16
.NET and Docker image optimizations for development versus production ........................................... 16
Architecting container and microservice-based applications .......................................... 18
Container design principles.............................................................................................................................................. 18
Containerizing monolithic applications ....................................................................................................................... 19
Deploying a monolithic application as a container............................................................................................ 21
Publishing a single-container-based application to Azure App Service.................................................... 21
ii Contents
Manage state and data in Docker applications........................................................................................................ 22
Service-oriented architecture........................................................................................................................................... 25
Microservices architecture................................................................................................................................................. 25
Additional resources ....................................................................................................................................................... 27
Data sovereignty per microservice ................................................................................................................................ 27
The relationship between microservices and the Bounded Context pattern........................................... 29
Logical architecture versus physical architecture..................................................................................................... 30
Challenges and solutions for distributed data management.............................................................................. 31
Challenge #1: How to define the boundaries of each microservice............................................................ 31
Challenge #2: How to create queries that retrieve data from several microservices............................ 32
Challenge #3: How to achieve consistency across multiple microservices............................................... 33
Challenge #4: How to design communication across microservice boundaries .................................... 35
Additional resources ....................................................................................................................................................... 36
Identify domain-model boundaries for each microservice.................................................................................. 36
The API gateway pattern versus the Direct client-to-microservice communication.................................. 40
Direct client-to-microservice communication ...................................................................................................... 40
Why consider API Gateways instead of direct client-to-microservice communication ....................... 41
What is the API Gateway pattern?............................................................................................................................. 42
Main features in the API Gateway pattern ............................................................................................................. 44
Using products with API Gateway features............................................................................................................ 45
Drawbacks of the API Gateway pattern................................................................................................................... 47
Additional resources ....................................................................................................................................................... 48
Communication in a microservice architecture ........................................................................................................ 48
Communication types .................................................................................................................................................... 49
Asynchronous microservice integration enforces microservice’s autonomy ........................................... 50
Communication styles.................................................................................................................................................... 52
Asynchronous message-based communication....................................................................................................... 54
Single-receiver message-based communication ................................................................................................ 55
Multiple-receivers message-based communication .......................................................................................... 56
Asynchronous event-driven communication........................................................................................................ 56
A note about messaging technologies for production systems ................................................................... 57
Resiliently publishing to the event bus ................................................................................................................... 58
iii Contents
Additional resources ....................................................................................................................................................... 58
Creating, evolving, and versioning microservice APIs and contracts............................................................... 59
Additional resources ....................................................................................................................................................... 59
Microservices addressability and the service registry ............................................................................................ 60
Additional resources ....................................................................................................................................................... 60
Creating composite UI based on microservices ....................................................................................................... 60
Additional resources ....................................................................................................................................................... 62
Resiliency and high availability in microservices...................................................................................................... 63
Health management and diagnostics in microservices .................................................................................... 63
Additional resources ....................................................................................................................................................... 65
Orchestrate microservices and multi-container applications for high scalability and availability ....... 66
Software platforms for container clustering, orchestration, and scheduling........................................... 68
Using container-based orchestrators in Microsoft Azure................................................................................ 68
Using Azure Kubernetes Service ................................................................................................................................ 68
Development environment for Kubernetes........................................................................................................... 69
Getting started with Azure Kubernetes Service (AKS) ....................................................................................... 70
Deploy with Helm charts into Kubernetes clusters............................................................................................. 70
Additional resources ....................................................................................................................................................... 71
Development process for Docker-based applications....................................................... 72
Development environment for Docker apps ............................................................................................................. 72
Development tool choices: IDE or editor................................................................................................................ 72
Additional resources ....................................................................................................................................................... 73
.NET languages and frameworks for Docker containers ....................................................................................... 73
Development workflow for Docker apps..................................................................................................................... 73
Workflow for developing Docker container-based applications .................................................................. 73
Step 1. Start coding and create your initial application or service baseline............................................. 75
Step 2. Create a Dockerfile related to an existing .NET base image............................................................ 76
Step 3. Create your custom Docker images and embed your application or service in them.......... 83
Step 4. Define your services in docker-compose.yml when building a multi-container Docker
application .......................................................................................................................................................................... 84
Step 5. Build and run your Docker application .................................................................................................... 86
Step 6. Test your Docker application using your local Docker host............................................................ 89
iv Contents
Simplified workflow when developing containers with Visual Studio ........................................................ 90
Using PowerShell commands in a Dockerfile to set up Windows Containers......................................... 91
Designing and Developing Multi-Container and Microservice-Based .NET Applications
................................................................................................................................................. 93
Design a microservice-oriented application .............................................................................................................. 93
Application specifications............................................................................................................................................. 93
Development team context ......................................................................................................................................... 94
Choosing an architecture.............................................................................................................................................. 94
Benefits of a microservice-based solution ............................................................................................................. 97
Downsides of a microservice-based solution ....................................................................................................... 98
External versus internal architecture and design patterns............................................................................... 99
The new world: multiple architectural patterns and polyglot microservices..........................................100
Creating a simple data-driven CRUD microservice ...............................................................................................102
Designing a simple CRUD microservice................................................................................................................102
Implementing a simple CRUD microservice with ASP.NET Core.................................................................103
The DB connection string and environment variables used by Docker containers.............................109
Generating Swagger description metadata from your ASP.NET Core Web API ...................................111
Defining your multi-container application with docker-compose.yml .........................................................116
Use a database server running as a container ........................................................................................................127
SQL Server running as a container with a microservice-related database..............................................128
Seeding with test data on Web application startup.........................................................................................129
EF Core InMemory database versus SQL Server running as a container .................................................132
Using a Redis cache service running in a container.........................................................................................132
Implementing event-based communication between microservices (integration events)...................133
Using message brokers and services buses for production systems ........................................................134
Integration events..........................................................................................................................................................135
The event bus ..................................................................................................................................................................135
Additional resources .....................................................................................................................................................138
Implementing an event bus with RabbitMQ for the development or test environment.......................138
Implementing a simple publish method with RabbitMQ...............................................................................139
Implementing the subscription code with the RabbitMQ API.....................................................................140
Additional resources .....................................................................................................................................................141
v Contents
Subscribing to events........................................................................................................................................................141
Publishing events through the event bus.............................................................................................................142
Idempotency in update message events..............................................................................................................149
Deduplicating integration event messages .........................................................................................................150
Testing ASP.NET Core services and web apps ........................................................................................................152
Testing in eShopOnContainers.................................................................................................................................155
Implement background tasks in microservices with IHostedService and the BackgroundService class
....................................................................................................................................................................................................157
Registering hosted services in your WebHost or Host ...................................................................................158
The IHostedService interface.....................................................................................................................................159
Implementing IHostedService with a custom hosted service class deriving from the
BackgroundService base class...................................................................................................................................160
Additional resources .....................................................................................................................................................163
Implement API Gateways with Ocelot ........................................................................................................................163
Architect and design your API Gateways..............................................................................................................163
Implementing your API Gateways with Ocelot ..................................................................................................168
Using Kubernetes Ingress plus Ocelot API Gateways......................................................................................180
Additional cross-cutting features in an Ocelot API Gateway .......................................................................181
Tackle Business Complexity in a Microservice with DDD and CQRS Patterns .............. 182
Apply simplified CQRS and DDD patterns in a microservice.............................................................................184
Additional resources .....................................................................................................................................................186
Apply CQRS and CQS approaches in a DDD microservice in eShopOnContainers .................................186
CQRS and DDD patterns are not top-level architectures...............................................................................187
Implement reads/queries in a CQRS microservice ................................................................................................188
Use ViewModels specifically made for client apps, independent from domain model constraints
...............................................................................................................................................................................................189
Use Dapper as a micro ORM to perform queries..............................................................................................189
Dynamic versus static ViewModels.........................................................................................................................190
Additional resources .....................................................................................................................................................193
Design a DDD-oriented microservice .........................................................................................................................194
Keep the microservice context boundaries relatively small ..........................................................................194
Layers in DDD microservices .....................................................................................................................................195
vi Contents
Design a microservice domain model ........................................................................................................................198
The Domain Entity pattern .........................................................................................................................................199
Implement a microservice domain model with .NET............................................................................................204
Domain model structure in a custom .NET Standard Library.......................................................................204
Structure aggregates in a custom .NET Standard library...............................................................................205
Implement domain entities as POCO classes .....................................................................................................206
Encapsulate data in the Domain Entities ..............................................................................................................207
Seedwork (reusable base classes and interfaces for your domain model)..................................................210
The custom Entity base class.....................................................................................................................................211
Repository contracts (interfaces) in the domain model layer ......................................................................212
Additional resources .....................................................................................................................................................213
Implement value objects..................................................................................................................................................213
Important characteristics of value objects ...........................................................................................................214
Value object implementation in C#........................................................................................................................215
How to persist value objects in the database with EF Core 2.0 and later................................................217
Persist value objects as owned entity types in EF Core 2.0 and later........................................................218
Additional resources .....................................................................................................................................................221
Use enumeration classes instead of enum types...................................................................................................222
Implement an Enumeration base class..................................................................................................................222
Additional resources .....................................................................................................................................................223
Design validations in the domain model layer .......................................................................................................223
Implement validations in the domain model layer...........................................................................................224
Additional resources .....................................................................................................................................................226
Client-side validation (validation in the presentation layers)............................................................................226
Additional resources .....................................................................................................................................................227
Domain events: design and implementation...........................................................................................................228
What is a domain event?.............................................................................................................................................228
Domain events versus integration events ............................................................................................................229
Domain events as a preferred way to trigger side effects across multiple aggregates within the
same domain ...................................................................................................................................................................229
Implement domain events..........................................................................................................................................231
Conclusions on domain events.................................................................................................................................238
vii Contents
Additional resources .....................................................................................................................................................239
Design the infrastructure persistence layer..............................................................................................................239
The Repository pattern ................................................................................................................................................240
Additional resources .....................................................................................................................................................243
Implement the infrastructure persistence layer with Entity Framework Core ............................................244
Introduction to Entity Framework Core.................................................................................................................244
Infrastructure in Entity Framework Core from a DDD perspective.............................................................245
Implement custom repositories with Entity Framework Core......................................................................246
EF DbContext and IUnitOfWork instance lifetime in your IoC container.................................................249
The repository instance lifetime in your IoC container...................................................................................249
Table mapping ................................................................................................................................................................250
Implement the Query Specification pattern........................................................................................................253
Use NoSQL databases as a persistence infrastructure.........................................................................................255
Introduction to Azure Cosmos DB and the native Cosmos DB API ...........................................................256
Implement .NET code targeting MongoDB and Azure Cosmos DB ..........................................................258
Design the microservice application layer and Web API ....................................................................................266
Use SOLID principles and Dependency Injection..............................................................................................266
Implement the microservice application layer using the Web API.................................................................267
Use Dependency Injection to inject infrastructure objects into your application layer.....................267
Implement the Command and Command Handler patterns .......................................................................271
The Command process pipeline: how to trigger a command handler.....................................................278
Implement the command process pipeline with a mediator pattern (MediatR) ..................................281
Apply cross-cutting concerns when processing commands with the Behaviors in MediatR ..........287
Implement resilient applications ....................................................................................... 291
Handle partial failure.........................................................................................................................................................292
Strategies to handle partial failure...............................................................................................................................294
Additional resources .....................................................................................................................................................295
Implement retries with exponential backoff ............................................................................................................295
Implement resilient Entity Framework Core SQL connections..........................................................................295
Execution strategies and explicit transactions using BeginTransaction and multiple DbContexts296
Additional resources .....................................................................................................................................................298
Use IHttpClientFactory to implement resilient HTTP requests .........................................................................298
viii Contents
Issues with the original HttpClient class available in .NET.............................................................................298
Benefits of using IHttpClientFactory.......................................................................................................................299
Multiple ways to use IHttpClientFactory...............................................................................................................300
How to use Typed Clients with IHttpClientFactory...........................................................................................300
Additional resources .....................................................................................................................................................303
Implement HTTP call retries with exponential backoff with IHttpClientFactory and Polly policies ...304
Add a jitter strategy to the retry policy.................................................................................................................305
Additional resources .....................................................................................................................................................305
Implement the Circuit Breaker pattern.......................................................................................................................305
Implement Circuit Breaker pattern with IHttpClientFactory and Polly .....................................................306
Test Http retries and circuit breakers in eShopOnContainers......................................................................307
Additional resources .....................................................................................................................................................309
Health monitoring ..............................................................................................................................................................309
Implement health checks in ASP.NET Core services ........................................................................................310
Use watchdogs................................................................................................................................................................314
Health checks when using orchestrators..............................................................................................................316
Advanced monitoring: visualization, analysis, and alerts ...............................................................................316
Additional resources .....................................................................................................................................................317
Make secure .NET Microservices and Web Applications................................................. 318
Implement authentication in .NET microservices and web applications ......................................................318
Authenticate with ASP.NET Core Identity.............................................................................................................319
Authenticate with external providers.....................................................................................................................321
Authenticate with bearer tokens..............................................................................................................................322
Authenticate with an OpenID Connect or OAuth 2.0 Identity provider...................................................323
Issue security tokens from an ASP.NET Core service.......................................................................................324
Consume security tokens............................................................................................................................................325
Additional resources..........................................................................................................................................................326
About authorization in .NET microservices and web applications..................................................................327
Implement role-based authorization .....................................................................................................................327
Implement policy-based authorization .................................................................................................................328
Additional resources .....................................................................................................................................................329
Store application secrets safely during development..........................................................................................329
ix Contents
Store secrets in environment variables .................................................................................................................330
Store secrets with the ASP.NET Core Secret Manager ....................................................................................330
Use Azure Key Vault to protect secrets at production time ..............................................................................331
Additional resources .....................................................................................................................................................332
.NET Microservices Architecture key takeaways.............................................................. 333
1 CHAPTER 1 | Introduction to Containers and Docker
CHAPTER 1
Introduction to Containers
and Docker
Containerization is an approach to software development in which an application or service, its
dependencies, and its configuration (abstracted as deployment manifest files) are packaged together
as a container image. The containerized application can be tested as a unit and deployed as a
container image instance to the host operating system (OS).
Just as shipping containers allow goods to be transported by ship, train, or truck regardless of the
cargo inside, software containers act as a standard unit of software deployment that can contain
different code and dependencies. Containerizing software this way enables developers and IT
professionals to deploy them across environments with little or no modification.
Containers also isolate applications from each other on a shared OS. Containerized applications run
on top of a container host that in turn runs on the OS (Linux or Windows). Containers therefore have a
significantly smaller footprint than virtual machine (VM) images.
Each container can run a whole web application or a service, as shown in Figure 2-1. In this example,
Docker host is a container host, and App1, App2, Svc 1, and Svc 2 are containerized applications or
services.
Figure 2-1. Multiple containers running on a container host
2 CHAPTER 1 | Introduction to Containers and Docker
Another benefit of containerization is scalability. You can scale out quickly by creating new containers
for short-term tasks. From an application point of view, instantiating an image (creating a container) is
similar to instantiating a process like a service or a web app. For reliability, however, when you run
multiple instances of the same image across multiple host servers, you typically want each container
(image instance) to run in a different host server or VM in different fault domains.
In short, containers offer the benefits of isolation, portability, agility, scalability, and control across the
whole application lifecycle workflow. The most important benefit is the environment’s isolation
provided between Dev and Ops.
What is Docker?
Docker is an open-source project for automating the deployment of applications as portable, self-
sufficient containers that can run on the cloud or on-premises. Docker is also a company that
promotes and evolves this technology, working in collaboration with cloud, Linux, and Windows
vendors, including Microsoft.
Figure 2-2. Docker deploys containers at all layers of the hybrid cloud.
Docker containers can run anywhere, on-premises in the customer datacenter, in an external service
provider or in the cloud, on Azure. Docker image containers can run natively on Linux and Windows.
However, Windows images can run only on Windows hosts and Linux images can run on Linux hosts
and Windows hosts (using a Hyper-V Linux VM, so far), where host means a server or a VM.
Developers can use development environments on Windows, Linux, or macOS. On the development
computer, the developer runs a Docker host where Docker images are deployed, including the app
and its dependencies. Developers who work on Linux or on macOS use a Docker host that is Linux
based, and they can create images only for Linux containers. (Developers working on macOS can edit
code or run the Docker CLI from macOS, but as of the time of this writing, containers don’t run
3 CHAPTER 1 | Introduction to Containers and Docker
directly on macOS.) Developers who work on Windows can create images for either Linux or Windows
Containers.
To host containers in development environments and provide additional developer tools, Docker
ships Docker Desktop for Windows or for macOS. These products install the necessary VM (the Docker
host) to host the containers.
To run Windows Containers, there are two types of runtimes:
• Windows Server Containers provide application isolation through process and namespace
isolation technology. A Windows Server Container shares a kernel with the container host and
with all containers running on the host.
• Hyper-V Containers expand on the isolation provided by Windows Server Containers by running
each container in a highly optimized virtual machine. In this configuration, the kernel of the
container host isn’t shared with the Hyper-V Containers, providing better isolation.
The images for these containers are created the same way and function the same. The difference is in
how the container is created from the image running a Hyper-V Container requires an extra
parameter. For details, see Hyper-V Containers.
Comparing Docker containers with virtual machines
Figure 2-3 shows a comparison between VMs and Docker containers.
Virtual Machines Docker Containers
4 CHAPTER 1 | Introduction to Containers and Docker
Virtual Machines Docker Containers
Virtual machines include the application, the
required libraries or binaries, and a full guest
operating system. Full virtualization requires
more resources than containerization.
Containers include the application and all its
dependencies. However, they share the OS kernel
with other containers, running as isolated
processes in user space on the host operating
system. (Except in Hyper-V containers, where
each container runs inside of a special virtual
machine per container.)
Figure 2-3. Comparison of traditional virtual machines to Docker containers
For VMs, there are three base layers in the host server, from the bottom-up: infrastructure, Host
Operating System and a Hypervisor and on top of all that each VM has its own OS and all necessary
libraries. For Docker, the host server only has the infrastructure and the OS and on top of that, the
container engine, that keeps container isolated but sharing the base OS services.
Because containers require far fewer resources (for example, they don’t need a full OS), they’re easy to
deploy and they start fast. This allows you to have higher density, meaning that it allows you to run
more services on the same hardware unit, thereby reducing costs.
As a side effect of running on the same kernel, you get less isolation than VMs.
The main goal of an image is that it makes the environment (dependencies) the same across different
deployments. This means that you can debug it on your machine and then deploy it to another
machine with the same environment guaranteed.
A container image is a way to package an app or service and deploy it in a reliable and reproducible
way. You could say that Docker isn’t only a technology but also a philosophy and a process.
When using Docker, you won’t hear developers say, “It works on my machine, why not in production?”
They can simply say, “It runs on Docker”, because the packaged Docker application can be executed
on any supported Docker environment, and it runs the way it was intended to on all deployment
targets (such as Dev, QA, staging, and production).
A simple analogy
Perhaps a simple analogy can help getting the grasp of the core concept of Docker.
Let’s go back in time to the 1950s for a moment. There were no word processors, and the
photocopiers were used everywhere (kind of).
Imagine you’re responsible for quickly issuing batches of letters as required, to mail them to
customers, using real paper and envelopes, to be delivered physically to each customer’s address
(there was no email back then).
At some point, you realize the letters are just a composition of a large set of paragraphs, which are
picked and arranged as needed, according to the purpose of the letter, so you devise a system to
issue letters quickly, expecting to get a hefty raise.
The system is simple:
1. You begin with a deck of transparent sheets containing one paragraph each.
5 CHAPTER 1 | Introduction to Containers and Docker
2. To issue a set of letters, you pick the sheets with the paragraphs you need, then you stack and
align them so they look and read fine.
3. Finally, you place the set in the photocopier and press start to produce as many letters as
required.
So, simplifying, that’s the core idea of Docker.
In Docker, each layer is the resulting set of changes that happen to the filesystem after executing a
command, such as, installing a program.
So, when you “look” at the filesystem after the layer has been copied, you see all the files, included in
the layer when the program was installed.
You can think of an image as an auxiliary read-only hard disk ready to be installed in a “computer”
where the operating system is already installed.
Similarly, you can think of a container as the “computer” with the image hard disk installed. The
container, just like a computer, can be powered on or off.
Docker terminology
This section lists terms and definitions you should be familiar with before getting deeper into Docker.
For further definitions, see the extensive glossary provided by Docker.
Container image: A package with all the dependencies and information needed to create a container.
An image includes all the dependencies (such as frameworks) plus deployment and execution
configuration to be used by a container runtime. Usually, an image derives from multiple base images
that are layers stacked on top of each other to form the container’s filesystem. An image is immutable
once it has been created.
Dockerfile: A text file that contains instructions for building a Docker image. It’s like a batch script,
the first line states the base image to begin with and then follow the instructions to install required
programs, copy files, and so on, until you get the working environment you need.
Build: The action of building a container image based on the information and context provided by its
Dockerfile, plus additional files in the folder where the image is built. You can build images with the
following Docker command:
docker build
Container: An instance of a Docker image. A container represents the execution of a single
application, process, or service. It consists of the contents of a Docker image, an execution
environment, and a standard set of instructions. When scaling a service, you create multiple instances
of a container from the same image. Or a batch job can create multiple containers from the same
image, passing different parameters to each instance.
Volumes: Offer a writable filesystem that the container can use. Since images are read-only but most
programs need to write to the filesystem, volumes add a writable layer, on top of the container image,
so the programs have access to a writable filesystem. The program doesn’t know it’s accessing a
6 CHAPTER 1 | Introduction to Containers and Docker
layered filesystem, it’s just the filesystem as usual. Volumes live in the host system and are managed
by Docker.
Tag: A mark or label you can apply to images so that different images or versions of the same image
(depending on the version number or the target environment) can be identified.
Multi-stage Build: Is a feature, since Docker 17.05 or higher, that helps to reduce the size of the final
images. For example, a large base image, containing the SDK can be used for compiling and
publishing and then a small runtime-only base image can be used to host the application.
Repository (repo): A collection of related Docker images, labeled with a tag that indicates the image
version. Some repos contain multiple variants of a specific image, such as an image containing SDKs
(heavier), an image containing only runtimes (lighter), etc. Those variants can be marked with tags. A
single repo can contain platform variants, such as a Linux image and a Windows image.
Registry: A service that provides access to repositories. The default registry for most public images is
Docker Hub (owned by Docker as an organization). A registry usually contains repositories from
multiple teams. Companies often have private registries to store and manage images they’ve created.
Azure Container Registry is another example.
Multi-arch image: For multi-architecture, it’s a feature that simplifies the selection of the appropriate
image, according to the platform where Docker is running. For example, when a Dockerfile requests a
base image FROM mcr.microsoft.com/dotnet/sdk:6.0 from the registry, it actually gets 6.0-
nanoserver-20H2, 6.0-nanoserver-1809 or 6.0-bullseye-slim, depending on the operating system
and version where Docker is running.
Docker Hub: A public registry to upload images and work with them. Docker Hub provides Docker
image hosting, public or private registries, build triggers and web hooks, and integration with GitHub
and Bitbucket.
Azure Container Registry: A public resource for working with Docker images and its components in
Azure. This provides a registry that’s close to your deployments in Azure and that gives you control
over access, making it possible to use your Azure Active Directory groups and permissions.
Docker Trusted Registry (DTR): A Docker registry service (from Docker) that can be installed on-
premises so it lives within the organization’s datacenter and network. It’s convenient for private
images that should be managed within the enterprise. Docker Trusted Registry is included as part of
the Docker Datacenter product.
Docker Desktop: Development tools for Windows and macOS for building, running, and testing
containers locally. Docker Desktop for Windows provides development environments for both Linux
and Windows Containers. The Linux Docker host on Windows is based on a Hyper-V virtual machine.
The host for Windows Containers is directly based on Windows. Docker Desktop for Mac is based on
the Apple Hypervisor framework and the xhyve hypervisor, which provides a Linux Docker host virtual
machine on macOS. Docker Desktop for Windows and for Mac replaces Docker Toolbox, which was
based on Oracle VirtualBox.
Compose: A command-line tool and YAML file format with metadata for defining and running multi-
container applications. You define a single application based on multiple images with one or more
.yml files that can override values depending on the environment. After you’ve created the definitions,
7 CHAPTER 1 | Introduction to Containers and Docker
you can deploy the whole multi-container application with a single command (docker-compose up)
that creates a container per image on the Docker host.
Cluster: A collection of Docker hosts exposed as if it were a single virtual Docker host, so that the
application can scale to multiple instances of the services spread across multiple hosts within the
cluster. Docker clusters can be created with Kubernetes, Azure Service Fabric, Docker Swarm and
Mesosphere DC/OS.
Orchestrator: A tool that simplifies the management of clusters and Docker hosts. Orchestrators
enable you to manage their images, containers, and hosts through a command-line interface (CLI) or a
graphical UI. You can manage container networking, configurations, load balancing, service discovery,
high availability, Docker host configuration, and more. An orchestrator is responsible for running,
distributing, scaling, and healing workloads across a collection of nodes. Typically, orchestrator
products are the same products that provide cluster infrastructure, like Kubernetes and Azure Service
Fabric, among other offerings in the market.
Docker containers, images, and registries
When using Docker, a developer creates an app or service and packages it and its dependencies into
a container image. An image is a static representation of the app or service and its configuration and
dependencies.
To run the app or service, the app’s image is instantiated to create a container, which will be running
on the Docker host. Containers are initially tested in a development environment or PC.
Developers should store images in a registry, which acts as a library of images and is needed when
deploying to production orchestrators. Docker maintains a public registry via Docker Hub; other
vendors provide registries for different collections of images, including Azure Container Registry.
Alternatively, enterprises can have a private registry on-premises for their own Docker images.
Figure 2-4 shows how images and registries in Docker relate to other components. It also shows the
multiple registry offerings from vendors.
8 CHAPTER 1 | Introduction to Containers and Docker
Figure 2-4. Taxonomy of Docker terms and concepts
The registry is like a bookshelf where images are stored and available to be pulled for building
containers to run services or web apps. There are private Docker registries on-premises and on the
public cloud. Docker Hub is a public registry maintained by Docker, along the Docker Trusted Registry
an enterprise-grade solution, Azure offers the Azure Container Registry. AWS, Google, and others also
have container registries.
Putting images in a registry lets you store static and immutable application bits, including all their
dependencies at a framework level. Those images can then be versioned and deployed in multiple
environments and therefore provide a consistent deployment unit.
Private image registries, either hosted on-premises or in the cloud, are recommended when:
• Your images must not be shared publicly due to confidentiality.
• You want to have minimum network latency between your images and your chosen deployment
environment. For example, if your production environment is Azure cloud, you probably want to
store your images in Azure Container Registry so that network latency will be minimal. In a
similar way, if your production environment is on-premises, you might want to have an on-
premises Docker Trusted Registry available within the same local network.
9 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
CHAPTER 2
Choosing Between .NET 6
and .NET Framework for
Docker Containers
There are two supported frameworks for building server-side containerized Docker applications with
.NET: .NET Framework and .NET 6. They share many .NET platform components, and you can share
code across the two. However, there are fundamental differences between them, and which
framework you use will depend on what you want to accomplish. This section provides guidance on
when to choose each framework.
General guidance
This section provides a summary of when to choose .NET 6 or .NET Framework. We provide more
details about these choices in the sections that follow.
Use .NET 6, with Linux or Windows Containers, for your containerized Docker server application when:
• You have cross-platform needs. For example, you want to use both Linux and Windows
Containers.
• Your application architecture is based on microservices.
• You need to start containers fast and want a small footprint per container to achieve better
density or more containers per hardware unit in order to lower your costs.
In short, when you create new containerized .NET applications, you should consider .NET 6 as the
default choice. It has many benefits and fits best with the containers philosophy and style of working.
An extra benefit of using .NET 6 is that you can run side-by-side .NET versions for applications within
the same machine. This benefit is more important for servers or VMs that do not use containers,
because containers isolate the versions of .NET that the app needs. (As long as they are compatible
with the underlying OS.)
Use .NET Framework for your containerized Docker server application when:
• Your application currently uses .NET Framework and has strong dependencies on Windows.
10 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
• You need to use Windows APIs that are not supported by .NET 6.
• You need to use third-party .NET libraries or NuGet packages that are not available for .NET 6.
Using .NET Framework on Docker can improve your deployment experiences by minimizing
deployment issues. This “lift and shift” scenario is important for containerizing legacy applications that
were originally developed with the traditional .NET Framework, like ASP.NET WebForms, MVC web
apps or WCF (Windows Communication Foundation) services.
Additional resources
• E-book: Modernize existing .NET Framework applications with Azure and Windows
Containers
https://aka.ms/liftandshiftwithcontainersebook
• Sample apps: Modernization of legacy ASP.NET web apps by using Windows Containers
https://aka.ms/eshopmodernizing
When to choose .NET for Docker containers
The modularity and lightweight nature of .NET 6 makes it perfect for containers. When you deploy
and start a container, its image is far smaller with .NET 6 than with .NET Framework. In contrast, to use
.NET Framework for a container, you must base your image on the Windows Server Core image, which
is a lot heavier than the Windows Nano Server or Linux images that you use for .NET 6.
Additionally, .NET 6 is cross-platform, so you can deploy server apps with Linux or Windows container
images. However, if you are using the traditional .NET Framework, you can only deploy images based
on Windows Server Core.
The following is a more detailed explanation of why to choose .NET 6.
Developing and deploying cross platform
Clearly, if your goal is to have an application (web app or service) that can run on multiple platforms
supported by Docker (Linux and Windows), the right choice is .NET 6, because .NET Framework only
supports Windows.
.NET 6 also supports macOS as a development platform. However, when you deploy containers to a
Docker host, that host must (currently) be based on Linux or Windows. For example, in a development
environment, you could use a Linux VM running on a Mac.
Visual Studio provides an integrated development environment (IDE) for Windows and supports
Docker development.
Visual Studio for Mac is an IDE, evolution of Xamarin Studio, that runs on macOS and supports
Docker-based application development. This tool should be the preferred choice for developers
working in Mac machines who also want to use a powerful IDE.
You can also use Visual Studio Code on macOS, Linux, and Windows. Visual Studio Code fully
supports .NET 6, including IntelliSense and debugging. Because VS Code is a lightweight editor, you
11 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
can use it to develop containerized apps on the machine in conjunction with the Docker CLI and the
.NET CLI. You can also target .NET 6 with most third-party editors like Sublime, Emacs, vi, and the
open-source OmniSharp project, which also provides IntelliSense support.
In addition to the IDEs and editors, you can use the .NET CLI for all supported platforms.
Using containers for new (“green-field”) projects
Containers are commonly used in conjunction with a microservices architecture, although they can
also be used to containerize web apps or services that follow any architectural pattern. You can use
.NET Framework on Windows Containers, but the modularity and lightweight nature of .NET 6 makes
it perfect for containers and microservices architectures. When you create and deploy a container, its
image is far smaller with .NET 6 than with .NET Framework.
Create and deploy microservices on containers
You could use the traditional .NET Framework for building microservices-based applications (without
containers) by using plain processes. That way, because the .NET Framework is already installed and
shared across processes, processes are light and fast to start. However, if you are using containers, the
image for the traditional .NET Framework is also based on Windows Server Core and that makes it too
heavy for a microservices-on-containers approach. However, teams have been looking for
opportunities to improve the experience for .NET Framework users as well. Recently, size of the
Windows Server Core container images have been reduced to >40% smaller.
On the other hand, .NET 6 is the best candidate if you’re embracing a microservices-oriented system
that is based on containers because .NET 6 is lightweight. In addition, its related container images, for
either Linux or Windows Nano Server, are lean and small, making containers light and fast to start.
A microservice is meant to be as small as possible: to be light when spinning up, to have a small
footprint, to have a small Bounded Context (check DDD, Domain-Driven Design), to represent a small
area of concerns, and to be able to start and stop fast. For those requirements, you will want to use
small and fast-to-instantiate container images like the .NET 6 container image.
A microservices architecture also allows you to mix technologies across a service boundary. This
approach enables a gradual migration to .NET 6 for new microservices that work in conjunction with
other microservices or with services developed with Node.js, Python, Java, GoLang, or other
technologies.
Deploying high density in scalable systems
When your container-based system needs the best possible density, granularity, and performance,
.NET and ASP.NET Core are your best options. ASP.NET Core is up to 10 times faster than ASP.NET in
the traditional .NET Framework, and it leads to other popular industry technologies for microservices,
such as Java servlets, Go, and Node.js.
This approach is especially relevant for microservices architectures, where you could have hundreds of
microservices (containers) running. With ASP.NET Core images (based on the .NET runtime) on Linux
or Windows Nano, you can run your system with a much lower number of servers or VMs, ultimately
saving costs in infrastructure and hosting.
12 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
When to choose .NET Framework for Docker
containers
While .NET 6 offers significant benefits for new applications and application patterns, .NET Framework
will continue to be a good choice for many existing scenarios.
Migrating existing applications directly to a Windows Server container
You might want to use Docker containers just to simplify deployment, even if you are not creating
microservices. For example, perhaps you want to improve your DevOps workflow with Docker—
containers can give you better isolated test environments and can also eliminate deployment issues
caused by missing dependencies when you move to a production environment. In cases like these,
even if you are deploying a monolithic application, it makes sense to use Docker and Windows
Containers for your current .NET Framework applications.
In most cases for this scenario, you will not need to migrate your existing applications to .NET 6; you
can use Docker containers that include the traditional .NET Framework. However, a recommended
approach is to use .NET 6 as you extend an existing application, such as writing a new service in
ASP.NET Core.
Using third-party .NET libraries or NuGet packages not available for
.NET 6
Third-party libraries are quickly embracing .NET Standard, which enables code sharing across all .NET
flavors, including .NET 6. With .NET Standard 2.0 and later, the API surface compatibility across
different frameworks has become significantly larger. Even more, .NET Core 2.x and newer applications
can also directly reference existing .NET Framework libraries (see .NET Framework 4.6.1 supporting
.NET Standard 2.0).
In addition, the Windows Compatibility Pack extends the API surface available for .NET Standard 2.0
on Windows. This pack allows recompiling most existing code to .NET Standard 2.x with little or no
modification, to run on Windows.
However, even with that exceptional progression since .NET Standard 2.0 and .NET Core 2.1 or later,
there might be cases where certain NuGet packages need Windows to run and might not support
.NET Core or later. If those packages are critical for your application, then you will need to use .NET
Framework on Windows Containers.
Using .NET technologies not available for .NET 6
Some .NET Framework technologies aren’t available in the current version of .NET (version 5.0 as of
this writing). Some of them might become available in later releases, but others don’t fit the new
application patterns targeted by .NET Core and might never be available.
The following list shows most of the technologies that aren’t available in .NET 6:
13 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
• ASP.NET Web Forms. This technology is only available on .NET Framework. Currently there are
no plans to bring ASP.NET Web Forms to .NET or later.
• WCF services. Even when a WCF-Client library is available to consume WCF services from .NET 6,
as of Jan-2021, the WCF server implementation is only available on .NET Framework.
• Workflow-related services. Windows Workflow Foundation (WF), Workflow Services (WCF + WF
in a single service), and WCF Data Services (formerly known as ADO.NET Data Services) are only
available on .NET Framework. There are currently no plans to bring them to .NET 6.
In addition to the technologies listed in the official .NET roadmap, other features might be ported to
the new unified .NET platform. You might consider participating in the discussions on GitHub so that
your voice can be heard. And if you think something is missing, file a new issue in the dotnet/runtime
GitHub repository.
Using a platform or API that doesn’t support .NET 6
Some Microsoft and third-party platforms don’t support .NET 6. For example, some Azure services
provide an SDK that isn’t yet available for consumption on .NET 6 yet. Most Azure SDK should
eventually be ported to .NET 6/.NET Standard, but some might not for several reasons. You can see
the available Azure SDKs in the Azure SDK Latest Releases page.
In the meantime, if any platform or service in Azure still doesn’t support .NET 6 with its client API, you
can use the equivalent REST API from the Azure service or the client SDK on .NET Framework.
Porting existing ASP.NET application to .NET 6
.NET Core is a revolutionary step forward from .NET Framework. It offers a host of advantages over
.NET Framework across the board from productivity to performance, and from cross-platform support
to developer satisfaction. If you are using .NET Framework and planning to migrate your application
to .NET Core or .NET 5+, see Porting Existing ASP.NET Apps to .NET Core.
Additional resources
• .NET fundamentals
https://docs.microsoft.com/dotnet/fundamentals
• Porting Projects to .NET 5
https://docs.microsoft.com/events/dotnetconf-2020/porting-projects-to-net-5
• .NET on Docker Guide
https://docs.microsoft.com/dotnet/core/docker/introduction
Decision table: .NET frameworks to use for Docker
The following decision table summarizes whether to use .NET Framework or .NET 6. Remember that
for Linux containers, you need Linux-based Docker hosts (VMs or servers) and that for Windows
Containers you need Windows Server based Docker hosts (VMs or servers).
14 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
Important
Your development machines will run one Docker host, either Linux or Windows. Related microservices
that you want to run and test together in one solution will all need to run on the same container
platform.
Architecture / App Type Linux containers Windows Containers
Microservices on containers .NET 6 .NET 6
Monolithic app .NET 6 .NET Framework
.NET 6
Best-in-class performance and
scalability
.NET 6 .NET 6
Windows Server legacy app
(“brown-field”) migration to
containers
– .NET Framework
New container-based
development (“green-field”)
.NET 6 .NET 6
ASP.NET Core .NET 6 .NET 6 (recommended)
.NET Framework
ASP.NET 4 (MVC 5, Web API 2,
and Web Forms)
– .NET Framework
SignalR services .NET Core 2.1 or higher
version
.NET Framework
.NET Core 2.1 or higher version
WCF, WF, and other legacy
frameworks
WCF in .NET Core (client
library only)
.NET Framework
WCF in .NET 6 (client library only)
Consumption of Azure services .NET 6
(eventually most Azure
services will provide client
SDKs for .NET 6)
.NET Framework
.NET 6
(eventually most Azure services
will provide client SDKs for .NET
6)
What OS to target with .NET containers
Given the diversity of operating systems supported by Docker and the differences between .NET
Framework and .NET 6, you should target a specific OS and specific versions depending on the
framework you are using.
For Windows, you can use Windows Server Core or Windows Nano Server. These Windows versions
provide different characteristics (IIS in Windows Server Core versus a self-hosted web server like
Kestrel in Nano Server) that might be needed by .NET Framework or .NET 6, respectively.
For Linux, multiple distros are available and supported in official .NET Docker images (like Debian).
In Figure 3-1, you can see the possible OS version depending on the .NET framework used.
15 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
Figure 3-1. Operating systems to target depending on versions of the .NET framework
When deploying legacy .NET Framework applications you have to target Windows Server Core,
compatible with legacy apps and IIS, but it has a larger image. When deploying .NET 6 applications,
you can target Windows Nano Server, which is cloud optimized, uses Kestrel and is smaller and starts
faster. You can also target Linux, supporting Debian, Alpine, and others. Also uses Kestrel, is smaller,
and starts faster.
You can also create your own Docker image in cases where you want to use a different Linux distro or
where you want an image with versions not provided by Microsoft. For example, you might create an
image with ASP.NET Core running on the traditional .NET Framework and Windows Server Core, which
is a not-so-common scenario for Docker.
When you add the image name to your Dockerfile file, you can select the operating system and
version depending on the tag you use, as in the following examples:
Image Comments
mcr.microsoft.com/dotnet/runtime:6.0 .NET 6 multi-architecture: Supports Linux and
Windows Nano Server depending on the Docker host.
mcr.microsoft.com/dotnet/aspnet:6.0 ASP.NET Core 6.0 multi-architecture: Supports Linux
and Windows Nano Server depending on the Docker
host.
The aspnetcore image has a few optimizations for
ASP.NET Core.
mcr.microsoft.com/dotnet/aspnet:6.0-
bullseye-slim
.NET 6 runtime-only on Linux Debian distro
16 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
Image Comments
mcr.microsoft.com/dotnet/aspnet:6.0-
nanoserver-1809
.NET 6 runtime-only on Windows Nano Server
(Windows Server version 1809)
Official .NET Docker images
The Official .NET Docker images are Docker images created and optimized by Microsoft. They are
publicly available in the Microsoft repositories on Docker Hub. Each repository can contain multiple
images, depending on .NET versions, and depending on the OS and versions (Linux Debian, Linux
Alpine, Windows Nano Server, Windows Server Core, etc.).
Since .NET Core 2.1, all the .NET Core or later images, including for ASP.NET Core are available at
Docker Hub at the .NET image repository: https://hub.docker.com/_/microsoft-dotnet/.
Since May 2018, Microsoft images are being syndicated in the Microsoft Container Registry. The
official catalog is still only available in Docker Hub, and there you’ll find the updated address to pull
the image.
Most image repositories provide extensive tagging to help you select not just a specific framework
version, but also to choose an OS (Linux distribution or Windows version).
.NET and Docker image optimizations for development versus
production
When building Docker images for developers, Microsoft focused on the following main scenarios:
• Images used to develop and build .NET apps.
• Images used to run .NET apps.
Why multiple images? When developing, building, and running containerized applications, you usually
have different priorities. By providing different images for these separate tasks, Microsoft helps
optimize the separate processes of developing, building, and deploying apps.
During development and build
During development, what is important is how fast you can iterate changes, and the ability to debug
the changes. The size of the image isn’t as important as the ability to make changes to your code and
see the changes quickly. Some tools and “build-agent containers”, use the development .NET image
(mcr.microsoft.com/dotnet/sdk:6.0) during development and build process. When building inside a
Docker container, the important aspects are the elements that are needed to compile your app. This
includes the compiler and any other .NET dependencies.
Why is this type of build image important? You don’t deploy this image to production. Instead, it’s an
image that you use to build the content you place into a production image. This image would be used
in your continuous integration (CI) environment or build environment when using Docker multi-stage
builds.
17 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers
In production
What is important in production is how fast you can deploy and start your containers based on a
production .NET image. Therefore, the runtime-only image based on
mcr.microsoft.com/dotnet/aspnet:6.0 is small so that it can travel quickly across the network from your
Docker registry to your Docker hosts. The contents are ready to run, enabling the fastest time from
starting the container to processing results. In the Docker model, there is no need for compilation
from C# code, as there is when you run dotnet build or dotnet publish when using the build container.
In this optimized image, you put only the binaries and other content needed to run the application.
For example, the content created by dotnet publish contains only the compiled .NET binaries,
images, .js, and .css files. Over time, you will see images that contain pre-jitted (the compilation from
IL to native that occurs at run time) packages.
Although there are multiple versions of the .NET and ASP.NET Core images, they all share one or more
layers, including the base layer. Therefore, the amount of disk space needed to store an image is
small; it consists only of the delta between your custom image and its base image. The result is that it
is quick to pull the image from your registry.
When you explore the .NET image repositories at Docker Hub, you will find multiple image versions
classified or marked with tags. These tags help to decide which one to use, depending on the version
you need, like those in the following table:
Image Comments
mcr.microsoft.com/dotnet/aspnet:6.0 ASP.NET Core, with runtime only and ASP.NET Core
optimizations, on Linux and Windows (multi-arch)
mcr.microsoft.com/dotnet/sdk:6.0 .NET 6, with SDKs included, on Linux and Windows (multi-
arch)
You can find all the available docker images in dotnet-docker and also refer to the latest preview
releases by using nightly build mcr.microsoft.com/dotnet/nightly/*
18 CHAPTER 3 | Architecting container and microservice-based applications
CHAPTER 3
Architecting container and
microservice-based
applications
Microservices offer great benefits but also raise huge new challenges. Microservice architecture patterns
are fundamental pillars when creating a microservice-based application.
Earlier in this guide, you learned basic concepts about containers and Docker. That information was
the minimum you needed to get started with containers. Even though containers are enablers of, and
a great fit for microservices, they aren’t mandatory for a microservice architecture. Many architectural
concepts in this architecture section could be applied without containers. However, this guide focuses
on the intersection of both due to the already introduced importance of containers.
Enterprise applications can be complex and are often composed of multiple services instead of a
single service-based application. For those cases, you need to understand other architectural
approaches, such as the microservices and certain Domain-Driven Design (DDD) patterns plus
container orchestration concepts. Note that this chapter describes not just microservices on
containers, but any containerized application, as well.
Container design principles
In the container model, a container image instance represents a single process. By defining a
container image as a process boundary, you can create primitives that can be used to scale or batch
the process.
When you design a container image, you’ll see an ENTRYPOINT definition in the Dockerfile. This
definition defines the process whose lifetime controls the lifetime of the container. When the process
completes, the container lifecycle ends. Containers might represent long-running processes like web
servers, but can also represent short-lived processes like batch jobs, which formerly might have been
implemented as Azure WebJobs.
If the process fails, the container ends, and the orchestrator takes over. If the orchestrator was
configured to keep five instances running and one fails, the orchestrator will create another container
instance to replace the failed process. In a batch job, the process is started with parameters. When the
process completes, the work is complete. This guidance drills-down on orchestrators, later on.
19 CHAPTER 3 | Architecting container and microservice-based applications
You might find a scenario where you want multiple processes running in a single container. For that
scenario, since there can be only one entry point per container, you could run a script within the
container that launches as many programs as needed. For example, you can use Supervisor or a
similar tool to take care of launching multiple processes inside a single container. However, even
though you can find architectures that hold multiple processes per container, that approach isn’t very
common.
Containerizing monolithic applications
You might want to build a single, monolithically deployed web application or service and deploy it as
a container. The application itself might not be internally monolithic, but structured as several
libraries, components, or even layers (application layer, domain layer, data-access layer, etc.).
Externally, however, it’s a single container—a single process, a single web application, or a single
service.
To manage this model, you deploy a single container to represent the application. To increase
capacity, you scale out, that is, just add more copies with a load balancer in front. The simplicity
comes from managing a single deployment in a single container or VM.
Figure 4-1. Example of the architecture of a containerized monolithic application
You can include multiple components, libraries, or internal layers in each container, as illustrated in
Figure 4-1. A monolithic containerized application has most of its functionality within a single
container, with internal layers or libraries, and scales out by cloning the container on multiple
servers/VMs. However, this monolithic pattern might conflict with the container principle “a container
does one thing, and does it in one process”, but might be ok for some cases.
The downside of this approach becomes evident if the application grows, requiring it to scale. If the
entire application can scale, it isn’t really a problem. However, in most cases, just a few parts of the
application are the choke points that require scaling, while other components are used less.
For example, in a typical e-commerce application, you likely need to scale the product information
subsystem, because many more customers browse products than purchase them. More customers use
20 CHAPTER 3 | Architecting container and microservice-based applications
their basket than use the payment pipeline. Fewer customers add comments or view their purchase
history. And you might have only a handful of employees that need to manage the content and
marketing campaigns. If you scale the monolithic design, all the code for these different tasks is
deployed multiple times and scaled at the same grade.
There are multiple ways to scale an application-horizontal duplication, splitting different areas of the
application, and partitioning similar business concepts or data. But, in addition to the problem of
scaling all components, changes to a single component require complete retesting of the entire
application, and a complete redeployment of all the instances.
However, the monolithic approach is common, because the development of the application is initially
easier than for microservices approaches. Thus, many organizations develop using this architectural
approach. While some organizations have had good enough results, others are hitting limits. Many
organizations designed their applications using this model because tools and infrastructure made it
too difficult to build service-oriented architectures (SOA) years ago, and they did not see the need-
until the application grew.
From an infrastructure perspective, each server can run many applications within the same host and
have an acceptable ratio of efficiency in resources usage, as shown in Figure 4-2.
Figure 4-2. Monolithic approach: Host running multiple apps, each app running as a container
Monolithic applications in Microsoft Azure can be deployed using dedicated VMs for each instance.
Additionally, using Azure virtual machine scale sets, you can easily scale the VMs. Azure App Service
can also run monolithic applications and easily scale instances without requiring you to manage the
VMs. Since 2016, Azure App Services can run single instances of Docker containers as well, simplifying
deployment.
As a QA environment or a limited production environment, you can deploy multiple Docker host VMs
and balance them using the Azure balancer, as shown in Figure 4-3. This lets you manage scaling with
a coarse-grain approach, because the whole application lives within a single container.
21 CHAPTER 3 | Architecting container and microservice-based applications
Figure 4-3. Example of multiple hosts scaling up a single container application
Deployment to the various hosts can be managed with traditional deployment techniques. Docker
hosts can be managed with commands like docker run or docker-compose performed manually, or
through automation such as continuous delivery (CD) pipelines.
Deploying a monolithic application as a container
There are benefits to using containers to manage monolithic application deployments. Scaling
container instances is far faster and easier than deploying additional VMs. Even if you use virtual
machine scale sets, VMs take time to start. When deployed as traditional application instances instead
of containers, the configuration of the application is managed as part of the VM, which isn’t ideal.
Deploying updates as Docker images is far faster and network efficient. Docker images typically start
in seconds, which speeds rollouts. Tearing down a Docker image instance is as easy as issuing a
docker stop command, and typically completes in less than a second.
Because containers are immutable by design, you never need to worry about corrupted VMs. In
contrast, update scripts for a VM might forget to account for some specific configuration or file left on
disk.
While monolithic applications can benefit from Docker, we’re touching only on the benefits.
Additional benefits of managing containers come from deploying with container orchestrators, which
manage the various instances and lifecycle of each container instance. Breaking up the monolithic
application into subsystems that can be scaled, developed, and deployed individually is your entry
point into the realm of microservices.
Publishing a single-container-based application to Azure App Service
Whether you want to get validation of a container deployed to Azure or when an application is simply
a single-container application, Azure App Service provides a great way to provide scalable single-
container-based services. Using Azure App Service is simple. It provides great integration with Git to
make it easy to take your code, build it in Visual Studio, and deploy it directly to Azure.
22 CHAPTER 3 | Architecting container and microservice-based applications
Figure 4-4. Publishing a single-container application to Azure App Service from Visual Studio 2022
Without Docker, if you needed other capabilities, frameworks, or dependencies that aren’t supported
in Azure App Service, you had to wait until the Azure team updated those dependencies in App
Service. Or you had to switch to other services like Azure Cloud Services or VMs, where you had
further control and you could install a required component or framework for your application.
Container support in Visual Studio 2017 and later gives you the ability to include whatever you want
in your application environment, as shown in Figure 4-4. Since you’re running it in a container, if you
add a dependency to your application, you can include the dependency in your Dockerfile or Docker
image.
As also shown in Figure 4-4, the publish flow pushes an image through a container registry. This can
be the Azure Container Registry (a registry close to your deployments in Azure and secured by Azure
Active Directory groups and accounts), or any other Docker registry, like Docker Hub or an on-
premises registry.
Manage state and data in Docker applications
In most cases, you can think of a container as an instance of a process. A process doesn’t maintain
persistent state. While a container can write to its local storage, assuming that an instance will be
around indefinitely would be like assuming that a single location in memory will be durable. You
23 CHAPTER 3 | Architecting container and microservice-based applications
should assume that container images, like processes, have multiple instances or will eventually be
killed. If they’re managed with a container orchestrator, you should assume that they might get
moved from one node or VM to another.
The following solutions are used to manage data in Docker applications:
From the Docker host, as Docker Volumes:
• Volumes are stored in an area of the host filesystem that’s managed by Docker.
• Bind mounts can map to any folder in the host filesystem, so access can’t be controlled from
Docker process and can pose a security risk as a container could access sensitive OS folders.
• tmpfs mounts are like virtual folders that only exist in the host’s memory and are never written
to the filesystem.
From remote storage:
• Azure Storage, which provides geo-distributable storage, providing a good long-term
persistence solution for containers.
• Remote relational databases like Azure SQL Database or NoSQL databases like Azure Cosmos
DB, or cache services like Redis.
From the Docker container:
• Overlay File System. This Docker feature implements a copy-on-write task that stores updated
information to the root file system of the container. That information is “on top” of the original
image on which the container is based. If the container is deleted from the system, those
changes are lost. Therefore, while it’s possible to save the state of a container within its local
storage, designing a system around this would conflict with the premise of container design,
which by default is stateless.
However, using Docker Volumes is now the preferred way to handle local data in Docker. If you need
more information about storage in containers check on Docker storage drivers and About storage
drivers.
The following provides more detail about these options:
Volumes are directories mapped from the host OS to directories in containers. When code in the
container has access to the directory, that access is actually to a directory on the host OS. This
directory is not tied to the lifetime of the container itself, and the directory is managed by Docker and
isolated from the core functionality of the host machine. Thus, data volumes are designed to persist
data independently of the life of the container. If you delete a container or an image from the Docker
host, the data persisted in the data volume isn’t deleted.
Volumes can be named or anonymous (the default). Named volumes are the evolution of Data
Volume Containers and make it easy to share data between containers. Volumes also support
volume drivers that allow you to store data on remote hosts, among other options.
Bind mounts are available since a long time ago and allow the mapping of any folder to a mount
point in a container. Bind mounts have more limitations than volumes and some important security
issues, so volumes are the recommended option.
24 CHAPTER 3 | Architecting container and microservice-based applications
tmpfs mounts are basically virtual folders that live only in the host’s memory and are never written to
the filesystem. They are fast and secure but use memory and are only meant for temporary, non-
persistent data.
As shown in Figure 4-5, regular Docker volumes can be stored outside of the containers themselves
but within the physical boundaries of the host server or VM. However, Docker containers can’t access
a volume from one host server or VM to another. In other words, with these volumes, it isn’t possible
to manage data shared between containers that run on different Docker hosts, although it could be
achieved with a volume driver that supports remote hosts.
Figure 4-5. Volumes and external data sources for container-based applications
Volumes can be shared between containers, but only in the same host, unless you use a remote driver
that supports remote hosts. In addition, when Docker containers are managed by an orchestrator,
containers might “move” between hosts, depending on the optimizations performed by the cluster.
Therefore, it isn’t recommended that you use data volumes for business data. But they’re a good
mechanism to work with trace files, temporal files, or similar that will not impact business data
consistency.
Remote data sources and cache tools like Azure SQL Database, Azure Cosmos DB, or a remote cache
like Redis can be used in containerized applications the same way they are used when developing
without containers. This is a proven way to store business application data.
Azure Storage. Business data usually will need to be placed in external resources or databases, like
Azure Storage. Azure Storage, in concrete, provides the following services in the cloud:
• Blob storage stores unstructured object data. A blob can be any type of text or binary data, such
as document or media files (images, audio, and video files). Blob storage is also referred to as
Object storage.
25 CHAPTER 3 | Architecting container and microservice-based applications
• File storage offers shared storage for legacy applications using standard SMB protocol. Azure
virtual machines and cloud services can share file data across application components via
mounted shares. On-premises applications can access file data in a share via the File service
REST API.
• Table storage stores structured datasets. Table storage is a NoSQL key-attribute data store,
which allows rapid development and fast access to large quantities of data.
Relational databases and NoSQL databases. There are many choices for external databases, from
relational databases like SQL Server, PostgreSQL, Oracle, or NoSQL databases like Azure Cosmos DB,
MongoDB, etc. These databases are not going to be explained as part of this guide since they are in a
completely different subject.
Service-oriented architecture
Service-oriented architecture (SOA) was an overused term and has meant different things to different
people. But as a common denominator, SOA means that you structure your application by
decomposing it into multiple services (most commonly as HTTP services) that can be classified as
different types like subsystems or tiers.
Those services can now be deployed as Docker containers, which solves deployment issues, because
all the dependencies are included in the container image. However, when you need to scale up SOA
applications, you might have scalability and availability challenges if you’re deploying based on single
Docker hosts. This is where Docker clustering software or an orchestrator can help you, as explained in
later sections where deployment approaches for microservices are described.
Docker containers are useful (but not required) for both traditional service-oriented architectures and
the more advanced microservices architectures.
Microservices derive from SOA, but SOA is different from microservices architecture. Features like
large central brokers, central orchestrators at the organization level, and the Enterprise Service Bus
(ESB) are typical in SOA. But in most cases, these are anti-patterns in the microservice community. In
fact, some people argue that “The microservice architecture is SOA done right.”
This guide focuses on microservices, because a SOA approach is less prescriptive than the
requirements and techniques used in a microservice architecture. If you know how to build a
microservice-based application, you also know how to build a simpler service-oriented application.
Microservices architecture
As the name implies, a microservices architecture is an approach to building a server application as a
set of small services. That means a microservices architecture is mainly oriented to the back-end,
although the approach is also being used for the front end. Each service runs in its own process and
communicates with other processes using protocols such as HTTP/HTTPS, WebSockets, or AMQP.
Each microservice implements a specific end-to-end domain or business capability within a certain
context boundary, and each must be developed autonomously and be deployable independently.
Finally, each microservice should own its related domain data model and domain logic (sovereignty
26 CHAPTER 3 | Architecting container and microservice-based applications
and decentralized data management) and could be based on different data storage technologies
(SQL, NoSQL) and different programming languages.
What size should a microservice be? When developing a microservice, size shouldn’t be the important
point. Instead, the important point should be to create loosely coupled services so you have
autonomy of development, deployment, and scale, for each service. Of course, when identifying and
designing microservices, you should try to make them as small as possible as long as you don’t have
too many direct dependencies with other microservices. More important than the size of the
microservice is the internal cohesion it must have and its independence from other services.
Why a microservices architecture? In short, it provides long-term agility. Microservices enable better
maintainability in complex, large, and highly-scalable systems by letting you create applications based
on many independently deployable services that each have granular and autonomous lifecycles.
As an additional benefit, microservices can scale out independently. Instead of having a single
monolithic application that you must scale out as a unit, you can instead scale out specific
microservices. That way, you can scale just the functional area that needs more processing power or
network bandwidth to support demand, rather than scaling out other areas of the application that
don’t need to be scaled. That means cost savings because you need less hardware.
Figure 4-6. Monolithic deployment versus the microservices approach
As Figure 4-6 shows, in the traditional monolithic approach, the application scales by cloning the
whole app in several servers/VM. In the microservices approach, functionality is segregated in smaller
services, so each service can scale independently. The microservices approach allows agile changes
and rapid iteration of each microservice, because you can change specific, small areas of complex,
large, and scalable applications.
Architecting fine-grained microservices-based applications enables continuous integration and
continuous delivery practices. It also accelerates delivery of new functions into the application. Fine-
grained composition of applications also allows you to run and test microservices in isolation, and to
27 CHAPTER 3 | Architecting container and microservice-based applications
evolve them autonomously while maintaining clear contracts between them. As long as you don’t
change the interfaces or contracts, you can change the internal implementation of any microservice or
add new functionality without breaking other microservices.
The following are important aspects to enable success in going into production with a microservices-
based system:
• Monitoring and health checks of the services and infrastructure.
• Scalable infrastructure for the services (that is, cloud and orchestrators).
• Security design and implementation at multiple levels: authentication, authorization, secrets
management, secure communication, etc.
• Rapid application delivery, usually with different teams focusing on different microservices.
• DevOps and CI/CD practices and infrastructure.
Of these, only the first three are covered or introduced in this guide. The last two points, which are
related to application lifecycle, are covered in the additional Containerized Docker Application
Lifecycle with Microsoft Platform and Tools e-book.
Additional resources
• Mark Russinovich. Microservices: An application revolution powered by the cloud
https://azure.microsoft.com/blog/microservices-an-application-revolution-powered-by-the-
cloud/
• Martin Fowler. Microservices
https://www.martinfowler.com/articles/microservices.html
• Martin Fowler. Microservice Prerequisites
https://martinfowler.com/bliki/MicroservicePrerequisites.html
• Jimmy Nilsson. Chunk Cloud Computing
https://www.infoq.com/articles/CCC-Jimmy-Nilsson
• Cesar de la Torre. Containerized Docker Application Lifecycle with Microsoft Platform and
Tools (downloadable e-book)
https://aka.ms/dockerlifecycleebook
Data sovereignty per microservice
An important rule for microservices architecture is that each microservice must own its domain data
and logic. Just as a full application owns its logic and data, so must each microservice own its logic
and data under an autonomous lifecycle, with independent deployment per microservice.
This means that the conceptual model of the domain will differ between subsystems or microservices.
Consider enterprise applications, where customer relationship management (CRM) applications,
28 CHAPTER 3 | Architecting container and microservice-based applications
transactional purchase subsystems, and customer support subsystems each call on unique customer
entity attributes and data, and where each employs a different Bounded Context (BC).
This principle is similar in Domain-driven design (DDD), where each Bounded Context or autonomous
subsystem or service must own its domain model (data plus logic and behavior). Each DDD Bounded
Context correlates to one business microservice (one or several services). This point about the
Bounded Context pattern is expanded in the next section.
On the other hand, the traditional (monolithic data) approach used in many applications is to have a
single centralized database or just a few databases. This is often a normalized SQL database that’s
used for the whole application and all its internal subsystems, as shown in Figure 4-7.
Figure 4-7. Data sovereignty comparison: monolithic database versus microservices
In the traditional approach, there’s a single database shared across all services, typically in a tiered
architecture. In the microservices approach, each microservice owns its model/data. The centralized
database approach initially looks simpler and seems to enable reuse of entities in different subsystems
to make everything consistent. But the reality is you end up with huge tables that serve many different
subsystems, and that include attributes and columns that aren’t needed in most cases. It’s like trying
to use the same physical map for hiking a short trail, taking a day-long car trip, and learning
geography.
A monolithic application with typically a single relational database has two important benefits: ACID
transactions and the SQL language, both working across all the tables and data related to your
application. This approach provides a way to easily write a query that combines data from multiple
tables.
However, data access becomes much more complicated when you move to a microservices
architecture. Even when using ACID transactions within a microservice or Bounded Context, it is crucial
to consider that the data owned by each microservice is private to that microservice and should only
29 CHAPTER 3 | Architecting container and microservice-based applications
be accessed either synchronously through its API endpoints(REST, gRPC, SOAP, etc) or asynchronously
via messaging(AMQP or similar).
Encapsulating the data ensures that the microservices are loosely coupled and can evolve
independently of one another. If multiple services were accessing the same data, schema updates
would require coordinated updates to all the services. This would break the microservice lifecycle
autonomy. But distributed data structures mean that you can’t make a single ACID transaction across
microservices. This in turn means you must use eventual consistency when a business process spans
multiple microservices. This is much harder to implement than simple SQL joins, because you can’t
create integrity constraints or use distributed transactions between separate databases, as we’ll
explain later on. Similarly, many other relational database features aren’t available across multiple
microservices.
Going even further, different microservices often use different kinds of databases. Modern
applications store and process diverse kinds of data, and a relational database isn’t always the best
choice. For some use cases, a NoSQL database such as Azure CosmosDB or MongoDB might have a
more convenient data model and offer better performance and scalability than a SQL database like
SQL Server or Azure SQL Database. In other cases, a relational database is still the best approach.
Therefore, microservices-based applications often use a mixture of SQL and NoSQL databases, which
is sometimes called the polyglot persistence approach.
A partitioned, polyglot-persistent architecture for data storage has many benefits. These include
loosely coupled services and better performance, scalability, costs, and manageability. However, it can
introduce some distributed data management challenges, as explained in “Identifying domain-model
boundaries” later in this chapter.
The relationship between microservices and the Bounded Context
pattern
The concept of microservice derives from the Bounded Context (BC) pattern in domain-driven design
(DDD). DDD deals with large models by dividing them into multiple BCs and being explicit about their
boundaries. Each BC must have its own model and database; likewise, each microservice owns its
related data. In addition, each BC usually has its own ubiquitous language to help communication
between software developers and domain experts.
Those terms (mainly domain entities) in the ubiquitous language can have different names in different
Bounded Contexts, even when different domain entities share the same identity (that is, the unique ID
that’s used to read the entity from storage). For instance, in a user-profile Bounded Context, the User
domain entity might share identity with the Buyer domain entity in the ordering Bounded Context.
A microservice is therefore like a Bounded Context, but it also specifies that it’s a distributed service.
It’s built as a separate process for each Bounded Context, and it must use the distributed protocols
noted earlier, like HTTP/HTTPS, WebSockets, or AMQP. The Bounded Context pattern, however,
doesn’t specify whether the Bounded Context is a distributed service or if it’s simply a logical
boundary (such as a generic subsystem) within a monolithic-deployment application.
It’s important to highlight that defining a service for each Bounded Context is a good place to start.
But you don’t have to constrain your design to it. Sometimes you must design a Bounded Context or
30 CHAPTER 3 | Architecting container and microservice-based applications
business microservice composed of several physical services. But ultimately, both patterns -Bounded
Context and microservice- are closely related.
DDD benefits from microservices by getting real boundaries in the form of distributed microservices.
But ideas like not sharing the model between microservices are what you also want in a Bounded
Context.
Additional resources
• Chris Richardson. Pattern: Database per service
https://microservices.io/patterns/data/database-per-service.html
• Martin Fowler. BoundedContext
https://martinfowler.com/bliki/BoundedContext.html
• Martin Fowler. PolyglotPersistence
https://martinfowler.com/bliki/PolyglotPersistence.html
• Alberto Brandolini. Strategic Domain Driven Design with Context Mapping
https://www.infoq.com/articles/ddd-contextmapping
Logical architecture versus physical architecture
It’s useful at this point to stop and discuss the distinction between logical architecture and physical
architecture, and how this applies to the design of microservice-based applications.
To begin, building microservices doesn’t require the use of any specific technology. For instance,
Docker containers aren’t mandatory to create a microservice-based architecture. Those microservices
could also be run as plain processes. Microservices is a logical architecture.
Moreover, even when a microservice could be physically implemented as a single service, process, or
container (for simplicity’s sake, that’s the approach taken in the initial version of eShopOnContainers),
this parity between business microservice and physical service or container isn’t necessarily required in
all cases when you build a large and complex application composed of many dozens or even
hundreds of services.
This is where there’s a difference between an application’s logical architecture and physical
architecture. The logical architecture and logical boundaries of a system do not necessarily map one-
to-one to the physical or deployment architecture. It can happen, but it often doesn’t.
Although you might have identified certain business microservices or Bounded Contexts, it doesn’t
mean that the best way to implement them is always by creating a single service (such as an ASP.NET
Web API) or single Docker container for each business microservice. Having a rule saying each
business microservice has to be implemented using a single service or container is too rigid.
Therefore, a business microservice or Bounded Context is a logical architecture that might coincide (or
not) with physical architecture. The important point is that a business microservice or Bounded
Context must be autonomous by allowing code and state to be independently versioned, deployed,
and scaled.
31 CHAPTER 3 | Architecting container and microservice-based applications
As Figure 4-8 shows, the catalog business microservice could be composed of several services or
processes. These could be multiple ASP.NET Web API services or any other kind of services using
HTTP or any other protocol. More importantly, the services could share the same data, as long as
these services are cohesive with respect to the same business domain.
Figure 4-8. Business microservice with several physical services
The services in the example share the same data model because the Web API service targets the same
data as the Search service. So, in the physical implementation of the business microservice, you’re
splitting that functionality so you can scale each of those internal services up or down as needed.
Maybe the Web API service usually needs more instances than the Search service, or vice versa.
In short, the logical architecture of microservices doesn’t always have to coincide with the physical
deployment architecture. In this guide, whenever we mention a microservice, we mean a business or
logical microservice that could map to one or more (physical) services. In most cases, this will be a
single service, but it might be more.
Challenges and solutions for distributed data
management
Challenge #1: How to define the boundaries of each microservice
Defining microservice boundaries is probably the first challenge anyone encounters. Each microservice
has to be a piece of your application and each microservice should be autonomous with all the
benefits and challenges that it conveys. But how do you identify those boundaries?
First, you need to focus on the application’s logical domain models and related data. Try to identify
decoupled islands of data and different contexts within the same application. Each context could have
a different business language (different business terms). The contexts should be defined and managed
independently. The terms and entities that are used in those different contexts might sound similar,
but you might discover that in a particular context, a business concept with one is used for a different
32 CHAPTER 3 | Architecting container and microservice-based applications
purpose in another context, and might even have a different name. For instance, a user can be
referred as a user in the identity or membership context, as a customer in a CRM context, as a buyer in
an ordering context, and so forth.
The way you identify boundaries between multiple application contexts with a different domain for
each context is exactly how you can identify the boundaries for each business microservice and its
related domain model and data. You always attempt to minimize the coupling between those
microservices. This guide goes into more detail about this identification and domain model design in
the section Identifying domain-model boundaries for each microservice later.
Challenge #2: How to create queries that retrieve data from several
microservices
A second challenge is how to implement queries that retrieve data from several microservices, while
avoiding chatty communication to the microservices from remote client apps. An example could be a
single screen from a mobile app that needs to show user information that’s owned by the basket,
catalog, and user identity microservices. Another example would be a complex report involving many
tables located in multiple microservices. The right solution depends on the complexity of the queries.
But in any case, you’ll need a way to aggregate information if you want to improve the efficiency in
the communications of your system. The most popular solutions are the following.
API Gateway. For simple data aggregation from multiple microservices that own different databases,
the recommended approach is an aggregation microservice referred to as an API Gateway. However,
you need to be careful about implementing this pattern, because it can be a choke point in your
system, and it can violate the principle of microservice autonomy. To mitigate this possibility, you can
have multiple fined-grained API Gateways each one focusing on a vertical “slice” or business area of
the system. The API Gateway pattern is explained in more detail in the API Gateway section later.
CQRS with query/reads tables. Another solution for aggregating data from multiple microservices is
the Materialized View pattern. In this approach, you generate, in advance (prepare denormalized data
before the actual queries happen), a read-only table with the data that’s owned by multiple
microservices. The table has a format suited to the client app’s needs.
Consider something like the screen for a mobile app. If you have a single database, you might pull
together the data for that screen using a SQL query that performs a complex join involving multiple
tables. However, when you have multiple databases, and each database is owned by a different
microservice, you cannot query those databases and create a SQL join. Your complex query becomes
a challenge. You can address the requirement using a CQRS approach—you create a denormalized
table in a different database that’s used just for queries. The table can be designed specifically for the
data you need for the complex query, with a one-to-one relationship between fields needed by your
application’s screen and the columns in the query table. It could also serve for reporting purposes.
This approach not only solves the original problem (how to query and join across microservices), but it
also improves performance considerably when compared with a complex join, because you already
have the data that the application needs in the query table. Of course, using Command and Query
Responsibility Segregation (CQRS) with query/reads tables means additional development work, and
you’ll need to embrace eventual consistency. Nonetheless, requirements on performance and high
33 CHAPTER 3 | Architecting container and microservice-based applications
scalability in collaborative scenarios (or competitive scenarios, depending on the point of view) are
where you should apply CQRS with multiple databases.
“Cold data” in central databases. For complex reports and queries that might not require real-time
data, a common approach is to export your “hot data” (transactional data from the microservices) as
“cold data” into large databases that are used only for reporting. That central database system can be
a Big Data-based system, like Hadoop, a data warehouse like one based on Azure SQL Data
Warehouse, or even a single SQL database that’s used just for reports (if size won’t be an issue).
Keep in mind that this centralized database would be used only for queries and reports that do not
need real-time data. The original updates and transactions, as your source of truth, have to be in your
microservices data. The way you would synchronize data would be either by using event-driven
communication (covered in the next sections) or by using other database infrastructure import/export
tools. If you use event-driven communication, that integration process would be similar to the way
you propagate data as described earlier for CQRS query tables.
However, if your application design involves constantly aggregating information from multiple
microservices for complex queries, it might be a symptom of a bad design -a microservice should be
as isolated as possible from other microservices. (This excludes reports/analytics that always should
use cold-data central databases.) Having this problem often might be a reason to merge
microservices. You need to balance the autonomy of evolution and deployment of each microservice
with strong dependencies, cohesion, and data aggregation.
Challenge #3: How to achieve consistency across multiple
microservices
As stated previously, the data owned by each microservice is private to that microservice and can only
be accessed using its microservice API. Therefore, a challenge presented is how to implement end-to-
end business processes while keeping consistency across multiple microservices.
To analyze this problem, let’s look at an example from the eShopOnContainers reference application.
The Catalog microservice maintains information about all the products, including the product price.
The Basket microservice manages temporal data about product items that users are adding to their
shopping baskets, which includes the price of the items at the time they were added to the basket.
When a product’s price is updated in the catalog, that price should also be updated in the active
baskets that hold that same product, plus the system should probably warn the user saying that a
particular item’s price has changed since they added it to their basket.
In a hypothetical monolithic version of this application, when the price changes in the products table,
the catalog subsystem could simply use an ACID transaction to update the current price in the Basket
table.
However, in a microservices-based application, the Product and Basket tables are owned by their
respective microservices. No microservice should ever include tables/storage owned by another
microservice in its own transactions, not even in direct queries, as shown in Figure 4-9.
34 CHAPTER 3 | Architecting container and microservice-based applications
Figure 4-9. A microservice can’t directly access a table in another microservice
The Catalog microservice shouldn’t update the Basket table directly, because the Basket table is
owned by the Basket microservice. To make an update to the Basket microservice, the Catalog
microservice should use eventual consistency probably based on asynchronous communication such
as integration events (message and event-based communication). This is how the eShopOnContainers
reference application performs this type of consistency across microservices.
As stated by the CAP theorem, you need to choose between availability and ACID strong consistency.
Most microservice-based scenarios demand availability and high scalability as opposed to strong
consistency. Mission-critical applications must remain up and running, and developers can work
around strong consistency by using techniques for working with weak or eventual consistency. This is
the approach taken by most microservice-based architectures.
Moreover, ACID-style or two-phase commit transactions are not just against microservices principles;
most NoSQL databases (like Azure Cosmos DB, MongoDB, etc.) do not support two-phase commit
transactions, typical in distributed databases scenarios. However, maintaining data consistency across
services and databases is essential. This challenge is also related to the question of how to propagate
changes across multiple microservices when certain data needs to be redundant—for example, when
you need to have the product’s name or description in the Catalog microservice and the Basket
microservice.
A good solution for this problem is to use eventual consistency between microservices articulated
through event-driven communication and a publish-and-subscribe system. These topics are covered
in the section Asynchronous event-driven communication later in this guide.
35 CHAPTER 3 | Architecting container and microservice-based applications
Challenge #4: How to design communication across microservice
boundaries
Communicating across microservice boundaries is a real challenge. In this context, communication
doesn’t refer to what protocol you should use (HTTP and REST, AMQP, messaging, and so on). Instead,
it addresses what communication style you should use, and especially how coupled your
microservices should be. Depending on the level of coupling, when failure occurs, the impact of that
failure on your system will vary significantly.
In a distributed system like a microservices-based application, with so many artifacts moving around
and with distributed services across many servers or hosts, components will eventually fail. Partial
failure and even larger outages will occur, so you need to design your microservices and the
communication across them considering the common risks in this type of distributed system.
A popular approach is to implement HTTP (REST)-based microservices, due to their simplicity. An
HTTP-based approach is perfectly acceptable; the issue here is related to how you use it. If you use
HTTP requests and responses just to interact with your microservices from client applications or from
API Gateways, that’s fine. But if you create long chains of synchronous HTTP calls across microservices,
communicating across their boundaries as if the microservices were objects in a monolithic
application, your application will eventually run into problems.
For instance, imagine that your client application makes an HTTP API call to an individual microservice
like the Ordering microservice. If the Ordering microservice in turn calls additional microservices using
HTTP within the same request/response cycle, you’re creating a chain of HTTP calls. It might sound
reasonable initially. However, there are important points to consider when going down this path:
• Blocking and low performance. Due to the synchronous nature of HTTP, the original request
doesn’t get a response until all the internal HTTP calls are finished. Imagine if the number of
these calls increases significantly and at the same time one of the intermediate HTTP calls to a
microservice is blocked. The result is that performance is impacted, and the overall scalability will
be exponentially affected as additional HTTP requests increase.
• Coupling microservices with HTTP. Business microservices shouldn’t be coupled with other
business microservices. Ideally, they shouldn’t “know” about the existence of other microservices.
If your application relies on coupling microservices as in the example, achieving autonomy per
microservice will be almost impossible.
• Failure in any one microservice. If you implemented a chain of microservices linked by HTTP
calls, when any of the microservices fails (and eventually they will fail) the whole chain of
microservices will fail. A microservice-based system should be designed to continue to work as
well as possible during partial failures. Even if you implement client logic that uses retries with
exponential backoff or circuit breaker mechanisms, the more complex the HTTP call chains are,
the more complex it is to implement a failure strategy based on HTTP.
In fact, if your internal microservices are communicating by creating chains of HTTP requests as
described, it could be argued that you have a monolithic application, but one based on HTTP between
processes instead of intra-process communication mechanisms.
Another Random Document on
Scribd Without Any Related Topics
symbolise splendour and glory, and in embroidered robes, which
suggest the patient use of the slow needle, and the variegated
harmony of colour attained at last. There is no marriage between
Christ and the soul, unless it is robed in the beauty of righteousness
and manifold graces of character. In other places we read that the
bride "made herself ready," and also that "to her was granted that
she should be arrayed in fine linen, clean and white," in which
sayings are set forth the double sources of such a garment of the
soul. It is a gift from above. It is "put on" by continual effort, based
on faith. The picture of the home-coming of the bride follows. She is
attended by her maidens, and with them she passes into the palace
amid joys and exultation. The psalm stops at the threshold. It is not
for the singer to draw back the curtains and let in the day. "The door
was shut." The presence of virgin companions waiting on the bride
no more interferes with the application of the psalm to Christ and
His Church than the similar representation brings confusion into our
Lord's parable of the Ten Virgins. Parables and symbols are elastic,
and often duplicate their representations of the same thing; and
such is the case here.
The closing verses are addressed, not to the bride, but to the king,
and can only in a very modified way and partially be supposed to
pass beyond the Jewish monarch and refer to the true King. Hopes
that he might be blessed with fortunate issue of the marriage were
quite in place in an epithalamion, and the delicacy of the light touch
with which this closing note is struck is noteworthy, especially in
contrast with the tone of many famous secular songs of similar
import. But much straining is needed to extract a spiritual sense
from the words. Perowne truly says that it is "wiser to acknowledge
at once the mixed character" of the psalm, and he quotes a
sagacious saying of Calvin's to the effect that it is not necessary that
every detail should be carefully fitted to Christ. The psalm had a
historical basis; and it has also a prophetic meaning, because the
king of Israel was himself a type, and Jesus Christ is the fulfilment of
the ideal never realised by its successive occupants. Both views of its
nature must be kept in view in its interpretation; and it need cause
no surprise if, at some points, the rind of prose fact is, so to speak,
thicker than at others, or if certain features absolutely refuse to lend
themselves to the spiritual interpretation.
PSALM XLVI.
1 God is a refuge and stronghold for us,
A help in troubles most readily to be found.
2 Therefore we will not fear, though the earth do change,
And the mountains reel into the heart of the sea.
3 Let its waters roar and foam;
Let mountains shake at its pride. Selah.
[Jehovah of hosts is with us;
A high tower for us is Jacob's God.]
4 [There is] a river—its branches make glad the city of God
The sanctuary of the tabernacles of the Most High.
5 God is in her midst; she shall not be moved:
God shall help her at the morning dawn.
6 Nations roared, kingdoms were moved:
He gave forth His voice, the earth melts.
7 Jehovah of hosts is with us;
A high tower for us is Jacob's God. Selah.
8 Come, behold the deeds of Jehovah,
Who has made desolations in the earth.
9 Quelling wars to the end of the earth:
The bow He breaks, and hews the spear in splinters;
The chariots He burns in the fire.
10 "Desist, and know that I am God:
I will be exalted in the nations, I will be exalted in the earth."
11 Jehovah of hosts is with us;
A high tower for us is Jacob's God. Selah.
There are two events, one or other of which probably supplies the
historical basis of this and the two following psalms. One is
Jehoshaphat's deliverance from the combined forces of the
bordering nations (2 Chron. xx.). Delitzsch adopts this as the
occasion of the psalm. But the other more usually accepted
reference to the destruction of Sennacherib's army is more probable.
Psalms xlvi. and xlviii. have remarkable parallelisms with Isaiah. The
noble contrast of the quiet river which makes glad the city of God
with a tossing, earth-shaking sea resembles the prophet's
threatening that the effect of refusing the "waters of Shiloah which
go softly" would be inundation by the strong and mighty river, the
Assyrian power. And the emblem is expanded in the striking
language of Isa. xxxiii. 21: "The glorious Lord will be unto us a place
of broad rivers and streams; wherein shall go no galley with oars."
Encircled by the flashing links of that broad moat, Jerusalem sits
secure. Again, the central thought of the refrain in the psalm, "The
Lord of hosts is with us," is closely allied to the symbolic name which
Isaiah gave as a pledge of deliverance, "Immanuel, God with us."
The structure is simple. The three strophes into which the psalm falls
set forth substantially the same thought, that God's presence is
safety and peace, whatever storms may roar. This general theme is
exhibited in the first strophe (vv. 1-3) in reference to natural
convulsions; in the second (vv. 4-7) in reference to the rage of
hostile kingdoms; and in the third (vv. 8-11) men are summoned to
behold a recent example of God's delivering might, which establishes
the truth of the preceding utterances and has occasioned the psalm.
The grand refrain which closes the second and third strophes should
probably be restored at the end of ver. 3.
In the first strophe the psalmist paints chaos come again, by the
familiar figures of a changed earth, tottering mountains sinking in
the raging sea from which they rose at creation, and a wild ocean
with thunderous dash appalling the ear and yeasty foam terrifying
the eye, sweeping in triumphant insolence over all the fair earth. It
is prosaic to insist on an allegorical meaning for the picture. It is
rather a vivid sketch of utter confusion, dashed in with three or four
bold strokes, an impossible case supposed in order to bring out the
unshaken calm of those who have God for ark in such a deluge. He
is not only a sure refuge and stronghold, but one easy of access
when troubles come. There is little good in a fortress, however
impregnable, if it is so difficult to reach that a fugitive might be slain
a hundred times before he was safe in it. But this high tower, which
no foe can scale, can be climbed at a thought, and a wish lifts us
within its mighty walls. The psalmist speaks a deep truth, verified in
the spiritual life of all ages, when he celebrates the refuge of the
devout soul as "most readily to be found."
As the text stands, this strophe is a verse too short, and ver. 3 drags
if connected with "will not we fear." The restoration of the refrain
removes the anomaly in the length of the strophe, and enables us to
detach ver. 3 from the preceding. Its sense is then completed, if we
regard it as the protasis of a sentence of which the refrain is the
apodosis, or if, with Cheyne and others, we take ver. 3, "Let its
waters roar," etc.—what of that? "Jehovah of hosts is with us." If the
strophe is thus completed, it conforms to the other two, in each of
which may be traced a division into two pairs of verses. These two
verse-pairs of the first strophe would then be inverted parallelism,—
the former putting security in God first, and surrounding trouble
second, the latter dealing with the same two subjects, but in
reversed sequence.
The second strophe brings a new picture to view with impressive
suddenness, which is even more vividly dramatic if the refrain is not
supplied. Right against the vision of confusion comes one of peace.
The abrupt introduction of "a river" as an isolated noun, which
dislocates grammatical structure, is almost an exclamation. "There is
a river" enfeebles the swing of the original. We might almost
translate, "Lo! a river!" Jerusalem was unique among historical cities
in that it had no great river. It had one tiny thread of water, of which
perhaps the psalmist is thinking. But whether there is here the same
contrast between Siloam's gentle flow and the surging waters of
hostile powers as Isaiah sets forth in the passage already referred to
(Isa. viii. 6), the meaning of this gladdening stream is the ever-
flowing communication of God Himself in His grace. The stream is
the fountain in flow. In the former strophe we hear the roar of the
troubled waters, and see the firm hills toppling into their depths.
Now we behold the gentle flow of the river, gliding through the city,
with music in its ripples and sunshine in its flash and refreshment in
its waters, parting into many arms and yet one in diversity, and
bringing life and gladness wherever it comes. Not with noise nor
tumult, but in silent communication, God's grace and peace refresh
the soul. Power is loud, but Omnipotence is silent. The roar of all the
billows is weak when compared with the quiet sliding onwards of
that still stream. It has its divisions. As in old days each man's bit of
garden was irrigated by a branch led from the stream, so in endless
diversity, corresponding to the infinite greatness of the source and
the innumerable variety of men's needs, God's grace comes. "All
these worketh that one and the selfsame Spirit, dividing to every
man severally." The streams gladden the city of God with the
gladness of satisfied thirsts, with the gladness which comes from the
contact of the human spirit with Divine completeness. So supplied,
the city may laugh at besiegers. It has unfailing supplies within itself,
and the enemy may cut off all surface streams, but its "water shall
be sure."
Substantially the same thought is next stated in plain words: "God is
in the midst of her." And therefore two things follow. One is
unshaken stability, and another is help at the right time—"at the turn
of the morning." "The Lord is in the midst of her"—that is a
perennial fact. "The Lord shall help her"—that is the "grace for
seasonable help." He, not we, determines when the night shall thin
away its blackness into morning twilight. But we may be sure that
the presence which is the pledge of stability and calm even in storm
and darkness will flash into energy of help at the moment when He
wills. The same expression is used to mark the time of His looking
from the pillar of cloud and troubling the Egyptians, and there may
be an allusion to that standing instance of His help here. "It is not
for you to know the times and the seasons"; but this we may know
—that the Lord of all times will always help at the right time; He will
not come so quickly as to anticipate our consciousness of need, nor
delay so long as to let us be irrevocably engulfed in the bog. "Jesus
loved Martha, and her sister, and Lazarus. When He heard therefore
that he was sick, He abode two days still in the same place where
He was." Yet He came in time.
With what vigour the short, crashing clauses of ver. 6 describe the
wrath and turbulence of the nations, and the instantaneous
dissolving of their strength into weakness at a word from those
awful lips! The verse may be taken as hypothetical or as historical.
In either case we see the sequence of events as by a succession of
lightning flashes. The hurry of the style, marked by the omission of
connecting particles, reflects the swiftness of incident, like Veni, vidi,
vici. The utterance of God's will conquers all. At the sound of that
voice stillness and a pause of dread fall on the "roar" (same word as
in ver. 3) of the nations, like the hush in the woods when thunder
rolls. He speaks, and all meaner sounds cease. "The lion hath
roared, who shall not fear?" No material vehicle is needed. To every
believer in God there is an incomprehensible action of the Divine Will
on material things; and no explanations bridge the gulf recognised in
the psalmist's broken utterances, which declare sequence and not
mode of operation: "He uttered His voice, the earth melted."
Again the triumph of the refrain peals forth, with its musical
accompaniment prolonging the impression. In it the psalmist gives
voice, for himself and his fellows, to their making their own of the
general truths which the psalm has been declaring. The two names
of God set forth a twofold ground for confidence. "Jehovah of hosts"
is all the more emphatic here since the Second Book of the Psalter is
usually Elohistic. It proclaims God's eternal, self-existent Being, and
His covenant relation, as well as His absolute authority over the
ranked forces of the universe, personal or impersonal, spiritual or
material. The Lord of all these legions is with us. When we say "The
God of Jacob," we reach back into the past and lay hold of the
Helper of the men of old as ours. What He has been, He is; what He
did, He is doing still. The river is full to-day, though the van of the
army did long ago drink and were satisfied. The bright waters are
still as pellucid and abundant as then, and the last of the rear-guard
will find them the same.
The third strophe summons to contemplate with fixed attention the
"desolations" made by some great manifestation of God's delivering
power. It is presupposed that these are still visible. Broken bows,
splintered spears, half-charred chariots, strew the ground, and Israel
can go forth without fear and feast their eyes on these tokens of
what God has done for them. The language is naturally applied to
the relics of Sennacherib's annihilated force. In any case it points to
a recent act of God's, the glad surprise of which palpitates all
through the psalm. The field of history is littered with broken,
abandoned weapons, once flourished in hands long since turned to
dust; and the city and throne of God against which they were lifted
remain unharmed. The voice which melted the earth speaks at the
close of the psalm; not now with destructive energy, but in warning,
through which tones of tenderness can be caught. God desires that
foes would cease their vain strife before it proves fatal. "Desist" is
here an elliptical expression, of which the full form is "Let your
hands drop"; or, as we say, "Ground your weapons," and learn how
vain is a contest with Him who is God, and whose fixed purpose is
that all nations shall know and exalt Him. The prospect hinted at in
the last words, of a world submissive to its King, softens the terrors
of His destructive manifestations, reveals their inmost purpose, and
opens to foes the possibility of passing, not as conquerors, but as
subjects, and therefore fellow-citizens, through the gate into the city.
PSALM XLVII.
1 All ye peoples, clap [your] hands;
Shout to God with joyful cry.
2 For Jehovah is most High [and] dread,
A great King over all the earth.
3 He subdues peoples under us,
And nations under our feet,
4 He chooses for us our inheritance,
The pride of Jacob whom He loved. Selah.
5 God is gone up with a shout,
Jehovah with trumpet clang.
6 Sing with the harp to God, sing with the harp:
Sing with the harp to our King, sing with the harp.
7 For King of all the earth is God:
Sing with the harp a skilful song.
8 God has become King over the nations:
He has taken His seat on His holy throne.
9 The princes of the peoples gather themselves together
[As] a people of the God of Abraham:
For to God belong the shields of the earth;
Greatly has He exalted Himself.
The closing thought of Psalm xlvi. is nobly expanded in this jubilant
summons to all nations to praise Jehovah as their King. Both psalms
have a similar, and probably the same, historical basis: a Divine act
so recent that the tumult of triumph has not yet subsided, and the
waves of joy still run high. Only in Psalm xlvi. the effect of that God-
wrought deliverance is principally regarded as the security and peace
of Israel, and in this psalm as the drawing of the nations to obey
Israel's King, and so to join the chorus of Israel's praise. While the
psalm has many resemblances to the Songs of the King (Psalm xciii.
seqq.), it is clearly in its right place here, as forming with the
preceding and succeeding psalms a trilogy, occasioned by one great
manifestation of God's care for the nation. No event is more
appropriate than the usually accepted destruction of Sennacherib's
army. The psalm has little of complexity in structure or thought. It is
a gush of pure rapture. It rises to prophetic foresight, and, by
reason of a comparatively small historical occasion, has a vision of
the world-wide expansion of the kingdom of God. It falls into two
strophes of four verses each, with one longer verse appended to the
latter.
In the first strophe the nations are invited to welcome God as their
King, not only because of His Divine exaltation and world-wide
dominion, but also because of His deeds for "Jacob." The same
Divine act which in Psalm xlvi. is represented as quelling wars and
melting the earth, and in Psalm xlviii. as bringing dismay, pain, and
flight, is here contemplated as attracting the nations to worship. The
psalmist knows that destructive providences have their gracious
aspect, and that God's true victory over men is not won when
opposition is crushed and hearts made to quake, but when
recognition of His sway and joy in it swell the heart. The quick
clatter of clapping hands in sign of homage to the King (2 Kings xi.
12) blends with the shrill cries with which Easterns express joy, in "a
tumult of acclaim." Hupfeld thinks that to suppose the heathen
called upon to do homage because of the victory for Israel won over
them is entirely mistaken. But unless that victory is the reason for
the summons, the psalm offers none; and it is surely not difficult to
suppose that the exhibition of God's power leads to reflection which
issues in recognition of His sovereignty. Vv. 3, 4, seem to state the
grounds for the summons in ver. 1. The tenses in these verses
present a difficulty in the way of taking them for a historical
retrospect of the conquest and partition of Canaan, which but for
that objection would be the natural interpretation. It is possible to
take them as "a truth of experience inferred from what had just
been witnessed, the historical fact being expressed not in historical
form, but generalised and idealised" (Delitzsch, in loc.). The just
accomplished deliverance repeated in essence the wonders of the
first entrance on possession of the land, and revealed the continuous
working of the same Divine hand, ever renewing the choice of
Jacob's inheritance, and ever scattering its enemies. "The pride of
Jacob" is a phrase in apposition with "our inheritance." The Holy
Land was the object of "pride" to "Jacob," not in an evil sense but in
that he boasted of it as a precious treasure intrusted to him by God.
The root fact of all God's ancient and continued blessings is that He
"loved." His own heart, not Jacob's deserts, prompted His mercies.
The second strophe is distinguished from the first by the increased
fervour of its calls to praise, by its still more exultant rush, and by its
omission of reference to Jacob. It is wholly concerned with the
peoples whom it invites to take up the song. As in the former
strophe the singer showed to the peoples God working in the world,
here he bids them look up and see Him ascending on high. "Now
that He ascended, what is it but that He also descended first?" The
mighty deliverance of which the triumph throbs through this trilogy
of pæans of victory was God's coming down. Now He has gone back
to His throne and seated Himself thereon, not as having ceased to
work in the world—for He is still King over it all—but as having
completed a delivering work. He does not withdraw when He goes
up. He does not cease to work here below when He sits throned in
His palace-temple above. The "shout" and "voice of a trumpet,"
which accompany that ascent, are borrowed from the ordinary
attendants on a triumphal procession. He soars as in a chariot of
praises,—from whose lips the psalm does not say, but probably it
intends Israel to be understood as the singer. To that choir the
nations are called to join their voices and harps, since God is their
King too, and not Jacob's only. The word rendered in the A.V. and
R.V. (text) "with understanding" is a noun, the name of a description
of psalm, which occurs in several psalm titles, and is best
understood as "a skilful song." Ver. 8 gathers up the reasons for the
peoples' homage to God. He has "become King" over them by His
recent act, having manifested and established His dominion; and He
has now "sat down on His throne," as having accomplished His
purpose, and as thence administering the world's affairs.
A final verse, of double the length of the others, stands somewhat
apart from the preceding strophe both in rhythm and in thought. It
crowns the whole. The invitations to the nations are conceived of as
having been welcomed and obeyed. And there rises before the
poet's eye a fair picture of a great convocation, such as might wait
before a world-ruling monarch's throne on the day of his coronation.
The princes of the nations, like tributary kings, come flocking to do
homage, "as if they surely knew their sovereign Lord was by."
The obliteration of distinction between Israel and the nations, by the
incorporation of the latter, so that "the peoples" become part of the
"people of the God of Abraham," floats before the singer's prophetic
eye, as the end of God's great manifestation of Himself. The two
parts of that double choir, which the preceding strophes summon to
song, coalesce at last, and in grand unison send up one full-
throated, universal melodious shout of praise. "The shields of the
earth" are best understood as a figurative expression for the princes
just spoken of, who now at last recognise to whom they belong.
Thus God has exalted Himself by His deeds; and the result of these
deeds is that He is greatly exalted by the praise of a world, in which
Israel and the "peoples" dwell as one beneath His sceptre and
celebrate His name.
The psalmist looked far ahead. His immediate experience was as "a
little window through which he saw great matters." The prophecy of
the universal spread of God's kingdom and the inclusion in it of the
Gentiles is Messianic; and whether the singer knew that he spoke of
a fair hope which should not be a fact for weary centuries, or
anticipated wider and permanent results from that triumph which
inspired his song, he spake of the Christ, and his strains are true
prophecies of His dominion. There is no intentional reference in the
psalm to the Ascension; but the thoughts underlying its picture of
God's going up with a shout are the same which that Ascension sets
forth as facts,—the merciful coming down into humanity of the
Divine Helper; the completeness of His victory as attested by His
return thither where He was before; His session in heaven, not as
idle nor wearied, but as having done what He meant to do; His
continuous working as King in the world; and the widening
recognition of His authority by loving hearts. The psalmist summons
us all to swell with our voices that great chorus of praise which, like
a sea, rolls and breaks in music round His royal seat.
PSALM XLVIII.
1 Great is Jehovah, and much to be praised,
In the city of our God, His holy mountain.
2 Lovely in loftiness, a joy of all the earth,
Is Mount Zion, the recesses of the north, the city of the great
King.
3 God in her palaces
Has made Himself known as a high tower.
4 For, lo, the kings assembled themselves,
They marched onwards together.
5 They saw, then they were amazed;
They were terror-struck, they fled.
6 Trembling seized them there;
Pain, as [of] a woman in travail.
7 With an east wind
Thou breakest the ships of Tarshish.
8 According as we have heard, so have we seen
In the city of Jehovah of hosts, in the city of our God:
God will establish her for ever. Selah.
9 We have thought, O God, of Thy loving-kindness
In the midst of Thy Temple.
10 According to Thy name, O God,
So is Thy praise to the ends of the earth:
Thy right hand is full of righteousness.
11 Let Mount Zion rejoice,
Let the daughters of Judah exult,
Because of Thy judgments.
12 Compass Zion, and walk round her:
Reckon her towers.
13 Give heed to her bulwark,
Pass through her palaces;
That ye may tell it to the generation after.
14 That such is God, our God:
For ever and aye He will guide us.
Al-Muth.
The situation seems the same as in Psalm xlvi., with which this
psalm has many points of contact. In both we have the same
triumph, the same proud affection for the holy city and sanctuary,
the same confidence in God's dwelling there, the same vivid
picturing of the mustering of enemies and their rapid dispersion, the
same swift movement of style in describing that overthrow, the
same thought of the diffusion of God's praise in the world as its
consequence, the same closing summons to look upon the tokens of
deliverance, with the difference that, in the former psalm, these are
the shattered weapons of the defeated foe, and in this the
unharmed battlements and palaces of the delivered city. The
emphatic word of the refrain in Psalm xlvi. also reappears here in
ver. 3. The psalm falls into three parts, of which the first (vv. 1, 2) is
introductory, celebrating the glory of Zion as the city of God; the
second (vv. 3-8) recounts in glowing words the deliverance of Zion;
and the third tells of the consequent praise and trust of the
inhabitants of Zion (vv. 9-14).
The general sense of the first part is plain, but ver. 2 is difficult.
"Mount Zion" is obviously subject, and "lovely in loftiness" and "joy
of all the earth" predicates; but the grammatical connection of the
two last clauses is obscure. Further, the meaning of "the sides of the
north" has not been satisfactorily ascertained. The supposition that
there is an allusion in the phrase to the mythological mountain of the
gods, with which Zion is compared, is surely most unnatural. Would
a Hebrew psalmist be likely to introduce such a parallel, even in
order to assert the superiority of Zion? Nor is the grammatical
objection to the supposition less serious. It requires a good deal of
stretching and inserting to twist the two words "the sides of the
north" into a comparison. It is more probable that the clause is
topographical, describing some part of the city, but what part is far
from clear. The accents make all the verse after "earth" the subject
of the two preceding predicates, and place a minor division at
"north," implying that "the sides of the north" is more closely
connected with "Mount Zion" than with the "city of the great King,"
or than that last clause is.
Following these indications, Stier renders "Mount Zion [and] the
northern side (i.e., the lower city, on the north of Zion), which
together make the city," etc. Others see here "the Holy City regarded
from three points of view"—viz., "the Mount Zion" (the city of David),
"the sides of the north" (Mount Moriah and the Temple), "the city of
the great King" (Jerusalem proper). So Perowne and others.
Delitzsch takes Zion to be the Temple hill, and "the sides of the
north" to be in apposition. "The Temple hill or Zion, in the narrower
sense, actually formed the north-eastern corner of ancient
Jerusalem," says he, and thus regards the subject of the whole
sentence as really twofold, not threefold, as appears at first—Zion on
the north, which is the palace-temple, and Jerusalem at its feet,
which is "the city of the great King." But it must be admitted that no
interpretation runs quite smoothly, though the summary ejection of
the troublesome words "the sides of the north" from the text is too
violent a remedy.
But the main thought of this first part is independent of such minute
difficulties. It is that the one thing which made Zion-Jerusalem
glorious was God's presence in it. It was beautiful in its elevation; it
was safely isolated from invaders by precipitous ravines, inclosing
the angle of the plateau on which it stood. But it was because God
dwelt there and manifested Himself there that it was "a joy for all
the earth." The name by which even the earthly Zion is called is
"Jehovah-Shammah, The Lord is there." We are not forcing New
Testament ideas into Old Testament words when we see in the
psalm an eternal truth. An idea is one thing; the fact which more or
less perfectly embodies it is another. The idea of God's dwelling with
men had its less perfect embodiment in the presence of the
Shechinah in the Temple, its more perfect in the dwelling of God in
the Church, and will have its complete when the city "having the
glory of God" shall appear, and He will dwell with men and be their
God. God in her, not anything of her own, makes Zion lovely and
gladdening. "Thy beauty was perfect through My comeliness which I
had put upon thee, saith the Lord."
The second part pictures Zion's deliverance with picturesque vigour
(vv. 3-8). Ver. 3 sums up the whole as the act of God, by which He
has made Himself known as that which the refrain of Psalm xlvi.
declared Him to be—a refuge, or, literally, a high tower. Then follows
the muster of the hosts. "The kings were assembled." That phrase
need not be called exaggeration, nor throw doubt on the reference
to Sennacherib's army, if we remember the policy of Eastern
conquerors in raising their armies from their conquests, and the
boast which Isaiah puts into the mouth of the Assyrian: "Are not my
princes altogether kings?" They advance against the city. "They
saw,"—no need to say what. Immediately they "were amazed." The
sight of the city broke on them from some hill-crest on their march.
Basilisk-like, its beauty was paralysing, and shot a nameless awe into
their hearts. "They were terror-struck; they fled." As in Psalm xlvi. 6,
the clauses, piled up without cement of connecting particles, convey
an impression of hurry, culminating in the rush of panic-struck
fugitives. As has been often noticed, they recall Cæsar's Veni, vidi,
vici; but these kings came, saw, were conquered. No cause for the
rout is named. No weapons were drawn in the city. An unseen hand
"smites once, and smites no more"; for once is enough. The process
of deliverance is not told; for a hymn of victory is not a chronicle.
One image explains it all, and signalises the Divine breath as the
sole agent. "Thou breakest the ships of Tarshish with an east wind"
is not history, but metaphor. The unwieldy, huge vessel, however
strong for fight, is unfit for storms, and, caught in a gale, rolls
heavily in the trough of the sea, and is driven on a lee shore and
ground to pieces on its rocks. "God blew upon them, and they were
scattered," as the medal struck on the defeat of the Armada had it.
In the companion psalm God's uttered voice did all. Here the breath
of the tempest, which is the breath of His lips, is the sole agent.
The past, of which the nation had heard from its fathers, lives again
in their own history; and that verification of traditional belief by
experience is to a devout soul the chief blessing of its deliverances.
There is rapture in the thought that "As we have heard, so have we
seen." The present ever seems commonplace. The sky is farthest
from earth right overhead, but touches the ground on the horizon
behind and before. Miracles were in the past; God will be manifestly
in the far-off future, but the present is apt to seem empty of Him.
But if we rightly mark His dealings with us, we shall learn that
nothing in His past has so passed that it is not present. As the
companion psalm says, "The God of Jacob is our refuge," this
exclaims, "As we have heard, so have we seen."
But not only does the deliverance link the present with the past, but
it flings a steady light into the future. "God shall establish her for
ever." The city is truly "the eternal city," because God dwells in it.
The psalmist was thinking of the duration of the actual Jerusalem,
the imperfect embodiment of a great idea. But whatever may be its
fate, the heart of his confidence is no false vision; for God's city will
outlast the world. Like the "maiden fortresses," of which there is one
in almost every land, fondly believed never to have been taken by
enemies, that city is inexpugnable, and the confident answer to
every threatening assailant is, "The virgin, the daughter of Zion,
hath despised thee, and laughed thee to scorn; the daughter of
Jerusalem hath shaken her head at thee." "God will establish her for
ever." The pledges of that stability are the deliverances of the past
and present.
The third part (vv. 9-14) deals with the praise and trust of the
inhabitants of Zion. Deliverance leads to thankful meditation on the
loving-kindness which it so signally displayed, and the ransomed
people first gather in the Temple, which was the scene of God's
manifestation of His grace, and therefore is the fitting place for them
to ponder it. The world-wide consequences of the great act of
loving-kindness almost shut out of sight for the moment its bearing
on the worshippers. It is a lofty height to which the song climbs,
when it regards national deliverance chiefly as an occasion for wider
diffusion of God's praise. His "name" is the manifestation of His
character in act. The psalmist is sure that wherever that character is
declared praise will follow, because he is sure that that character is
perfectly and purely good, and that God cannot act but in such a
way as to magnify Himself. That great sea will cast up nothing but
pearls. The words carry also a lesson for recipients of Divine loving-
kindness, teaching them that they misapprehend the purpose of
their blessings, if they confine these to their own well-being and lose
sight of the higher object—that men may learn to know and love
Him. But the deliverance not only produces grateful meditation and
widespread praise; it sets the mother city and her daughter villages
astir, like Miriam and her maidens, with timbrel and dance, and
ringing songs which celebrate "Thy judgments," terrible as they
were. That dead host was an awful sight, and hymns of praise seem
heartless for its dirge. But it is not savage glee nor fierce hatred
which underlies the psalmist's summons, and still less is it selfish joy.
"Thy judgments" are to be hymned when they smite some giant evil;
and when systems and their upholders that array themselves against
God are drowned in some Red Sea, it is fitting that on its banks
should echo, "Sing ye to Jehovah, for He hath triumphed gloriously."
The close of this part may be slightly separated from vv. 9-11. The
citizens who have been cooped up by the siege are bidden to come
forth, and, free from fear, to compass the city without, and pass
between its palaces within, and so see how untouched they are. The
towers and bulwark or rampart remain unharmed, with not a stone
smitten from its place. Within, the palaces stand without a trace of
damage to their beauty. Whatever perishes in any assaults, that
which is of God will abide; and, after all musterings of the enemy,
the uncaptured walls will rise in undiminished strength, and the fair
palaces which they guard glitter in untarnished splendour. And this
complete exemption from harm is to be told to the generation
following, that they may learn what a God this God is, and how
safely and well He will guide all generations.
The last word in the Hebrew text, which the A.V. and R.V. render
"even unto death," can scarcely have that meaning. Many attempts
have been made to find a signification appropriate to the close of
such a triumphal hymn as this, but the simplest and most probable
course is to regard the words as a musical note, which is either
attached abnormally to the close of the psalm, or has strayed hither
from the superscription of Psalm xlix. It is found in the superscription
of Psalm ix. ("Al-Muth") as a musical direction, and has in all
likelihood the same meaning here. If it is removed, the psalm ends
abruptly, but a slight transposition of words and change of the main
division of the verse remove that difficulty by bringing "for ever and
aye" from the first half. The change improves both halves, laying the
stress of the first exclusively on the thought that this God is such a
God (or, by another rendering, "is here," i.e., in the city), without
bringing in reference to the eternity of His protection, and
completing the second half worthily, with the thought of His eternal
guidance of the people among whom He dwells.
PSALM XLIX.
1 Hear this, all ye peoples;
Give ear, all ye inhabitants of the world:
2 Both low-born and high-born,
Rich and poor together.
3 My mouth shall speak wisdom;
And the meditation of my heart shall utter understanding
4 I will bend my ear to a parable:
I will open my riddle on the harp.
5 Why should I fear in the days of evil,
When the malice of my pursuers surrounds me,
6 [Even of] those who rely on their riches,
And boast of their wealth?
7 No man can at all redeem a brother;
He cannot give to God a ransom for him
8 (Yea, too costly is the redemption price of their soul,
And he must leave it alone for ever):
9 That he may continue living on for ever,
And may not see the pit.
10 Nay, he must see that the wise die
The fool and the brutish perish alike,
And leave to others their riches.
11 Their inward thought [is that] their houses [shall last] for ever,
Their dwellings to generation after generation;
They call their lands by their own names.
12 But man [being] in honour abides not:
He becomes like the beasts [that] are brought to silence.
13 This is the lot of them to whom presumptuous confidence
belongs:
And after them men approve their sayings. Selah.
14 Like sheep they are folded in Sheol;
Death shepherds them:
And the upright shall rule over them in the morning;
And their form shall be wasted away by Sheol,
So that it is without a dwelling.
15 Surely God shall redeem my soul from the power of Sheol:
For He shall take me. Selah.
16 Fear not thou when a man becomes rich,
When the glory of his house increases:
17 For when he dies he will not take away any [of it];
His glory shall not go down after him.
18 Though in his lifetime he bless his soul
(And [men] praise thee when thou doest well for thyself)
19 He shall go to the generation of his fathers;
For evermore they see not light.
20 Man [who is] in honour, and has not understanding,
Becomes like the beasts that are brought to silence.
This psalm touches the high-water mark of Old Testament faith in a
future life; and in that respect, as well as in its application of that
faith to alleviate the mystery of present inequalities and non-
correspondence of desert with condition, is closely related to the
noble Psalm lxxiii., with which it has also several verbal identities.
Both have the same problem before them—to construct a theodicy,
or "to vindicate the ways of God to man"—and both solve it in the
same fashion. Both appear to refer to the story of Enoch in their
remarkable expression for ultimate reception into the Divine
presence. But whether the psalms are contemporaneous cannot be
determined from these data. Cheyne regards the treatment of the
theme in Psalm lxxiii. as "more skilful," and therefore presumably
later than Psalm xlix., which he would place "somewhat before the
close of the Persian period." This date rests on the assumption that
the amount of certitude as to a future life expressed in the psalm
was not realised in Israel till after the exile.
After a solemn summons to all the world to hear the psalmist's
utterance of what he has learned by Divine teaching (vv. 1-4), the
psalm is divided into two parts, each closed with a refrain. The
former of these (vv. 5-12) contrasts the arrogant security of the
prosperous godless with the end that awaits them; while the second
(vv. 13-20) contrasts the dreary lot of these victims of vain self-
confidence with the blessed reception after death into God's own
presence which the psalmist grasped as a certainty for himself, and
thereon bases an exhortation to possess souls in patience while the
godless prosper, and to be sure that their lofty structures will topple
into hideous ruin.
The psalmist's consciousness that he speaks by Divine inspiration,
and that his message imports all men, is grandly expressed in his
introductory summons. The very name which he gives to the world
suggests the latter thought; for it means—the world considered as
fleeting. Since we dwell in so transitory an abode, it becomes us to
listen to the deep truths of the psalm. These have a message for
high and low, for rich and poor. They are like a keen lancet to let out
too great fulness of blood from the former, and to teach moderation,
lowliness, and care for the Unseen. They are a calming draught for
the latter, soothing when perplexed or harmed by "the proud man's
contumely." But the psalmist calls for universal attention, not only
because his lessons fit all classes, but because they are in
themselves "wisdom," and because he himself had first bent his ear
to receive them before he strung his lyre to utter them. The brother-
psalmist, in Psalm lxxiii., presents himself as struggling with doubt
and painfully groping his way to his conclusion. This psalmist
presents himself as a divinely inspired teacher, who has received into
purged and attentive ears; in many a whisper from God, and as the
result of many an hour of silent waiting, the word which he would
now proclaim on the housetops. The discipline of the teacher of
religious truth is the same at all times. There must be the bent ear
before there is the message which men will recognise as important
and true.
There is no parable in the ordinary sense in the psalm. The word
seems to have acquired the wider meaning of a weighty didactic
utterance, as in Psalm lxxviii. 2. The expression "Open my riddle" is
ambiguous, and is by some understood to mean the proposal and by
others the solution of the puzzle; but the phrase is more naturally
understood of solving than of setting a riddle, and if so, the
disproportion between the characters and fortunes of good and bad
is the mystery or riddle, and the psalm is its solution.
The main theme of the first part is the certainty of death, which
makes infinitely ludicrous the rich man's arrogance. It is one version
of
"There is no armour against Fate;
Death lays his icy hand on kings."
Therefore how vain the boasting in wealth, when all its heaps cannot
buy a day of life! This familiar thought is not all the psalmist's
contribution to the solution of the mystery of life's unequal partition
of worldly good; but it prepares the way for it, and it lays a
foundation for his refusal to be afraid, however pressed by insolent
enemies. Very significantly he sets the conclusion, to which
observation of the transiency of human prosperity has led him, at
the beginning of his "parable." In the parallel psalm (lxxiii.) the
singer shows himself struggling from the depths of perplexity up to
the sunny heights of faith. But here the poet begins with the clear
utterance of trustful courage, and then vindicates it by the thought
of the impotence of wealth to avert death.
The hostility to himself of the self-confident rich boasters appears
only for a moment at first. It is described by a gnarled, energetic
phrase which has been diversely understood. But it seems clear that
the "iniquity" (A.V. and R.V.) spoken of in ver. 5 b is not the
psalmist's sin, for a reference here to his guilt or to retribution would
be quite irrelevant; and if it were the consequences of his own evil
that dogged him at his heels, he had every reason to fear, and
confidence would be insolent defiance. But the word rendered in the
A.V. heels, which is retained in the R.V. with a change in
construction, may be a participial noun, derived from a verb
meaning to trip up or supplant; and this gives a natural coherence to
the whole verse, and connects it with the following one. "Pursuers"
is a weak equivalent for the literal "those who would supplant me,"
but conveys the meaning, though in a somewhat enfeebled
condition. Ver. 6 is a continuance of the description of the
supplanters. They are "men of this world," the same type of man as
excites stern disapproval in many psalms: as, for instance, in xvii. 14
—a psalm which is closely related to this, both in its portrait of the
godless and its lofty hope for the future. It is to be noted that they
are not described as vicious or God-denying or defying. They are
simply absorbed in the material, and believe that land and money
are the real, solid goods. They are the same men as Jesus meant
when He said that it was hard for those who trusted in riches to
enter into the kingdom of heaven. It has been thought that the
existence of such a class points to a late date for the psalm; but the
reliance on riches does not require large riches to rely on, and may
flourish in full perniciousness in very primitive social conditions. A
small elevation suffices to lift a man high enough above his fellows
to make a weak head giddy. Those to whom material possessions
are the only good have a natural enmity towards those who find
their wealth in truth and goodness. The poet, the thinker, and, most
of all, the religious man, are targets for more or less active "malice,"
or, at all events, are recognised as belonging to another class, and
regarded as singular and "unpractical," if nothing worse. But the
psalmist looks far enough ahead to see the end of all the boasting,
and points to the great instance of the impotence of material good—
its powerlessness to prolong life. It would be more natural to find in
ver. 7 the statement that the rich man cannot prolong his own days
than that he cannot do so for a "brother." A very slight change in the
text would make the initial word of the verse ("brother") the particle
of asseveration, which occurs in ver. 15 (the direct antithesis of this
verse), and is characteristic of the parallel Psalm lxxiii. With that
reading (Ewald, Cheyne, Baethgen, etc.) other slight difficulties are
smoothed; but the present text is attested by the LXX. and other
Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.
More than just a book-buying platform, we strive to be a bridge
connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.
Join us on a journey of knowledge exploration, passion nurturing, and
personal growth every day!
ebookbell.com

More Related Content

PDF
Net Microservices Architecture For Containerized Net Applications V70 Updated...
PDF
NET Microservices Architecture for Containerized NET Applications Cesar De La...
PDF
Containerized docker application lifecycle with microsoft platform and tools ...
PDF
Containerized docker application lifecycle with microsoft platform and tools ...
PPTX
Net core microservice development made easy with azure dev spaces
PPTX
Micro services
PPTX
Modernizing existing .NET applications with Windows Containers and Azure cloud
PPTX
Going MicroServices with Net
Net Microservices Architecture For Containerized Net Applications V70 Updated...
NET Microservices Architecture for Containerized NET Applications Cesar De La...
Containerized docker application lifecycle with microsoft platform and tools ...
Containerized docker application lifecycle with microsoft platform and tools ...
Net core microservice development made easy with azure dev spaces
Micro services
Modernizing existing .NET applications with Windows Containers and Azure cloud
Going MicroServices with Net

Similar to Net Microservices Architecture For Containerized Net Applications V60 Updated To Aspnet Core 60 Cesar De La Torre (20)

PPTX
Azure Modern Cloud App Development Approaches 2017
PDF
Microservices for Application Modernisation
PPTX
Docker & aPaaS: Enterprise Innovation and Trends for 2015
PPTX
Architecture evolution
PDF
.NET Cloud-Native Bootcamp- Los Angeles
PPT
Webinar_102317_Heffner.ppt
PPTX
Disruptive Trends in Application Development
PDF
"Portrait of the developer as The Artist" Lockheed Architect Workshop
PPTX
The Microservices world in. NET Core and. NET framework
PPTX
App Modernization: From 0 to Hero
PDF
Cloud Computing as Innovation Hub - Mohammad Fairus Khalid
PPTX
Container Orchestration for .NET Developers
PDF
AWS Innovate: Moving Microsoft .Net applications one container at a time - Da...
PDF
Embracing Containers and Microservices for Future Proof Application Moderniza...
PDF
Building Cloud-Native Microservices with .NET 8 and Minimal APIs.pdf
PDF
Dapr for-net-developers
PDF
Containers, microservices and serverless for realists
PPTX
Designing Microservices
PPTX
Building cloud native apps
PDF
Azure-Container-Apps.pdf
Azure Modern Cloud App Development Approaches 2017
Microservices for Application Modernisation
Docker & aPaaS: Enterprise Innovation and Trends for 2015
Architecture evolution
.NET Cloud-Native Bootcamp- Los Angeles
Webinar_102317_Heffner.ppt
Disruptive Trends in Application Development
"Portrait of the developer as The Artist" Lockheed Architect Workshop
The Microservices world in. NET Core and. NET framework
App Modernization: From 0 to Hero
Cloud Computing as Innovation Hub - Mohammad Fairus Khalid
Container Orchestration for .NET Developers
AWS Innovate: Moving Microsoft .Net applications one container at a time - Da...
Embracing Containers and Microservices for Future Proof Application Moderniza...
Building Cloud-Native Microservices with .NET 8 and Minimal APIs.pdf
Dapr for-net-developers
Containers, microservices and serverless for realists
Designing Microservices
Building cloud native apps
Azure-Container-Apps.pdf
Ad

Recently uploaded (20)

PDF
The Lost Whites of Pakistan by Jahanzaib Mughal.pdf
PDF
RMMM.pdf make it easy to upload and study
PDF
Saundersa Comprehensive Review for the NCLEX-RN Examination.pdf
PDF
Classroom Observation Tools for Teachers
PDF
Basic Mud Logging Guide for educational purpose
PDF
VCE English Exam - Section C Student Revision Booklet
PDF
01-Introduction-to-Information-Management.pdf
PPTX
BOWEL ELIMINATION FACTORS AFFECTING AND TYPES
PDF
2.FourierTransform-ShortQuestionswithAnswers.pdf
PDF
Abdominal Access Techniques with Prof. Dr. R K Mishra
PPTX
Pharmacology of Heart Failure /Pharmacotherapy of CHF
PDF
ANTIBIOTICS.pptx.pdf………………… xxxxxxxxxxxxx
PDF
Microbial disease of the cardiovascular and lymphatic systems
PPTX
Final Presentation General Medicine 03-08-2024.pptx
PPTX
The Healthy Child – Unit II | Child Health Nursing I | B.Sc Nursing 5th Semester
PPTX
Introduction to Child Health Nursing – Unit I | Child Health Nursing I | B.Sc...
PPTX
Introduction_to_Human_Anatomy_and_Physiology_for_B.Pharm.pptx
PDF
grade 11-chemistry_fetena_net_5883.pdf teacher guide for all student
PPTX
Renaissance Architecture: A Journey from Faith to Humanism
PDF
TR - Agricultural Crops Production NC III.pdf
The Lost Whites of Pakistan by Jahanzaib Mughal.pdf
RMMM.pdf make it easy to upload and study
Saundersa Comprehensive Review for the NCLEX-RN Examination.pdf
Classroom Observation Tools for Teachers
Basic Mud Logging Guide for educational purpose
VCE English Exam - Section C Student Revision Booklet
01-Introduction-to-Information-Management.pdf
BOWEL ELIMINATION FACTORS AFFECTING AND TYPES
2.FourierTransform-ShortQuestionswithAnswers.pdf
Abdominal Access Techniques with Prof. Dr. R K Mishra
Pharmacology of Heart Failure /Pharmacotherapy of CHF
ANTIBIOTICS.pptx.pdf………………… xxxxxxxxxxxxx
Microbial disease of the cardiovascular and lymphatic systems
Final Presentation General Medicine 03-08-2024.pptx
The Healthy Child – Unit II | Child Health Nursing I | B.Sc Nursing 5th Semester
Introduction to Child Health Nursing – Unit I | Child Health Nursing I | B.Sc...
Introduction_to_Human_Anatomy_and_Physiology_for_B.Pharm.pptx
grade 11-chemistry_fetena_net_5883.pdf teacher guide for all student
Renaissance Architecture: A Journey from Faith to Humanism
TR - Agricultural Crops Production NC III.pdf
Ad

Net Microservices Architecture For Containerized Net Applications V60 Updated To Aspnet Core 60 Cesar De La Torre

  • 1. Net Microservices Architecture For Containerized Net Applications V60 Updated To Aspnet Core 60 Cesar De La Torre download https://ebookbell.com/product/net-microservices-architecture-for- containerized-net-applications-v60-updated-to-aspnet- core-60-cesar-de-la-torre-44435496 Explore and download more ebooks at ebookbell.com
  • 2. Here are some recommended products that we believe you will be interested in. You can click the link to download. Net Microservices Architecture For Containerized Net Applications V70 Updated To Aspnet Core 70 Cesar De La Torre https://ebookbell.com/product/net-microservices-architecture-for- containerized-net-applications-v70-updated-to-aspnet-core-70-cesar-de- la-torre-54560584 Net Microservices Architecture For Containerized Net Applications V10 Cesar De La Torre https://ebookbell.com/product/net-microservices-architecture-for- containerized-net-applications-v10-cesar-de-la-torre-5641962 Software Architecture With C 10 And Net 6 Develop Software Solutions Using Microservices Devops Ef Core And Design Patterns For Azure 3rd Edition 3rd Gabriel Baptista https://ebookbell.com/product/software-architecture-with-c-10-and- net-6-develop-software-solutions-using-microservices-devops-ef-core- and-design-patterns-for-azure-3rd-edition-3rd-gabriel- baptista-47994386 Software Architecture With C 9 And Net 5 Architecting Software Solutions Using Microservices Devops And Design Patterns For Azure Second Edition 2nd Edition Gabriel Baptista https://ebookbell.com/product/software-architecture-with-c-9-and- net-5-architecting-software-solutions-using-microservices-devops-and- design-patterns-for-azure-second-edition-2nd-edition-gabriel- baptista-50847388
  • 3. Software Architecture With C 12 And Net 8 Build Enterprise Applications Using Microservices Devops Ef Core And Design Patterns For Azure 4th Edition https://ebookbell.com/product/software-architecture-with-c-12-and- net-8-build-enterprise-applications-using-microservices-devops-ef- core-and-design-patterns-for-azure-4th-edition-59103730 Handson Software Architecture With C 8 And Net Core 3 Architecting Software Solutions Using Microservices Devops And Design Patterns For Azure Cloud 1st Edition Gabriel Baptista https://ebookbell.com/product/handson-software-architecture- with-c-8-and-net-core-3-architecting-software-solutions-using- microservices-devops-and-design-patterns-for-azure-cloud-1st-edition- gabriel-baptista-50851904 Handson Software Architecture With C 8 And Net Core 3 Architecting Software Solutions Using Microservices Devops And Design Patterns For Azure Cloud Gabriel Baptista Francesco Abbruzzese https://ebookbell.com/product/handson-software-architecture- with-c-8-and-net-core-3-architecting-software-solutions-using- microservices-devops-and-design-patterns-for-azure-cloud-gabriel- baptista-francesco-abbruzzese-11074032 Microservices By Examples Using Net Core A Book With Lot Of Practical And Architectural Styles For Microservices Using Net Core Biswa Pujarini Mohapatra https://ebookbell.com/product/microservices-by-examples-using-net- core-a-book-with-lot-of-practical-and-architectural-styles-for- microservices-using-net-core-biswa-pujarini-mohapatra-27587818 Implementing Eventdriven Microservices Architecture In Net 7 Develop Eventbased Distributed Apps That Can Scale With Everchanging Business Demands Using C 11 And Net 7 1st Edition Joshua Garverick https://ebookbell.com/product/implementing-eventdriven-microservices- architecture-in-net-7-develop-eventbased-distributed-apps-that-can- scale-with-everchanging-business-demands-using-c-11-and-net-7-1st- edition-joshua-garverick-48224172
  • 6. EDITION v6.0 - Updated to ASP.NET Core 6.0 Refer changelog for the book updates and community contributions. This guide is an introduction to developing microservices-based applications and managing them using containers. It discusses architectural design and implementation approaches using .NET and Docker containers. To make it easier to get started, the guide focuses on a reference containerized and microservice- based application that you can explore. The reference application is available at the eShopOnContainers GitHub repo. Action links • This e-book is also available in a PDF format (English version only) Download • Clone/Fork the reference application eShopOnContainers on GitHub • Watch the introductory video • Get to know the Microservices Architecture right away Introduction Enterprises are increasingly realizing cost savings, solving deployment problems, and improving DevOps and production operations by using containers. Microsoft has been releasing container innovations for Windows and Linux by creating products like Azure Kubernetes Service and Azure Service Fabric, and by partnering with industry leaders like Docker, Mesosphere, and Kubernetes. These products deliver container solutions that help companies build and deploy applications at cloud speed and scale, whatever their choice of platform or tools. Docker is becoming the de facto standard in the container industry, supported by the most significant vendors in the Windows and Linux ecosystems. (Microsoft is one of the main cloud vendors supporting Docker.) In the future, Docker will probably be ubiquitous in any datacenter in the cloud or on-premises. In addition, the microservices architecture is emerging as an important approach for distributed mission-critical applications. In a microservice-based architecture, the application is built on a collection of services that can be developed, tested, deployed, and versioned independently. About this guide This guide is an introduction to developing microservices-based applications and managing them using containers. It discusses architectural design and implementation approaches using .NET and Docker containers. To make it easier to get started with containers and microservices, the guide focuses on a reference containerized and microservice-based application that you can explore. The sample application is available at the eShopOnContainers GitHub repo.
  • 7. This guide provides foundational development and architectural guidance primarily at a development environment level with a focus on two technologies: Docker and .NET. Our intention is that you read this guide when thinking about your application design without focusing on the infrastructure (cloud or on-premises) of your production environment. You will make decisions about your infrastructure later, when you create your production-ready applications. Therefore, this guide is intended to be infrastructure agnostic and more development-environment-centric. After you have studied this guide, your next step would be to learn about production-ready microservices on Microsoft Azure. Version This guide has been revised to cover .NET 6 version along with many additional updates related to the same “wave” of technologies (that is, Azure and additional third-party technologies) coinciding in time with the .NET 6 release. That’s why the book version has also been updated to version 6.0. What this guide does not cover This guide does not focus on the application lifecycle, DevOps, CI/CD pipelines, or team work. The complementary guide Containerized Docker Application Lifecycle with Microsoft Platform and Tools focuses on that subject. The current guide also does not provide implementation details on Azure infrastructure, such as information on specific orchestrators. Additional resources • Containerized Docker Application Lifecycle with Microsoft Platform and Tools (downloadable e-book) https://aka.ms/dockerlifecycleebook Who should use this guide We wrote this guide for developers and solution architects who are new to Docker-based application development and to microservices-based architecture. This guide is for you if you want to learn how to architect, design, and implement proof-of-concept applications with Microsoft development technologies (with special focus on .NET) and with Docker containers. You will also find this guide useful if you are a technical decision maker, such as an enterprise architect, who wants an architecture and technology overview before you decide on what approach to select for new and modern distributed applications. How to use this guide The first part of this guide introduces Docker containers, discusses how to choose between .NET 6 and the .NET Framework as a development framework, and provides an overview of microservices. This content is for architects and technical decision makers who want an overview but don’t need to focus on code implementation details.
  • 8. The second part of the guide starts with the Development process for Docker based applications section. It focuses on the development and microservice patterns for implementing applications using .NET and Docker. This section will be of most interest to developers and architects who want to focus on code and on patterns and implementation details. Related microservice and container-based reference application: eShopOnContainers The eShopOnContainers application is an open-source reference app for .NET and microservices that is designed to be deployed using Docker containers. The application consists of multiple subsystems, including several e-store UI front-ends (a Web MVC app, a Web SPA, and a native mobile app). It also includes the back-end microservices and containers for all required server-side operations. The purpose of the application is to showcase architectural patterns. IT IS NOT A PRODUCTION- READY TEMPLATE to start real-world applications. In fact, the application is in a permanent beta state, as it’s also used to test new potentially interesting technologies as they show up. Send us your feedback! We wrote this guide to help you understand the architecture of containerized applications and microservices in .NET. The guide and related reference application will be evolving, so we welcome your feedback! If you have comments about how this guide can be improved, submit feedback at https://aka.ms/ebookfeedback. Credits Co-Authors: Cesar de la Torre, Sr. PM, .NET product team, Microsoft Corp. Bill Wagner, Sr. Content Developer, C+E, Microsoft Corp. Mike Rousos, Principal Software Engineer, DevDiv CAT team, Microsoft Editors: Mike Pope Steve Hoag Participants and reviewers: Jeffrey Richter, Partner Software Eng, Azure team, Microsoft Jimmy Bogard, Chief Architect at Headspring Udi Dahan, Founder & CEO, Particular Software Jimmy Nilsson, Co-founder and CEO of Factor10
  • 9. Glenn Condron, Sr. Program Manager, ASP.NET team Mark Fussell, Principal PM Lead, Azure Service Fabric team, Microsoft Diego Vega, PM Lead, Entity Framework team, Microsoft Barry Dorrans, Sr. Security Program Manager Rowan Miller, Sr. Program Manager, Microsoft Ankit Asthana, Principal PM Manager, .NET team, Microsoft Scott Hunter, Partner Director PM, .NET team, Microsoft Nish Anil, Sr. Program Manager, .NET team, Microsoft Dylan Reisenberger, Architect and Dev Lead at Polly Steve “ardalis” Smith - Software Architect and Trainer - Ardalis.com Ian Cooper, Coding Architect at Brighter Unai Zorrilla, Architect and Dev Lead at Plain Concepts Eduard Tomas, Dev Lead at Plain Concepts Ramon Tomas, Developer at Plain Concepts David Sanz, Developer at Plain Concepts Javier Valero, Chief Operating Officer at Grupo Solutio Pierre Millet, Sr. Consultant, Microsoft Michael Friis, Product Manager, Docker Inc Charles Lowell, Software Engineer, VS CAT team, Microsoft Miguel Veloso, Software Development Engineer at Plain Concepts Sumit Ghosh, Principal Consultant at Neudesic Copyright PUBLISHED BY Microsoft Developer Division, .NET and Visual Studio product teams A division of Microsoft Corporation One Microsoft Way Redmond, Washington 98052-6399 Copyright © 2022 by Microsoft Corporation
  • 10. All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by any means without the written permission of the publisher. This book is provided “as-is” and expresses the author’s views and opinions. The views, opinions and information expressed in this book, including URL and other Internet website references, may change without notice. Some examples depicted herein are provided for illustration only and are fictitious. No real association or connection is intended or should be inferred. Microsoft and the trademarks listed at https://www.microsoft.com on the “Trademarks” webpage are trademarks of the Microsoft group of companies. Mac and macOS are trademarks of Apple Inc. The Docker whale logo is a registered trademark of Docker, Inc. Used by permission. All other marks and logos are property of their respective owners.
  • 11. i Contents Contents Introduction to Containers and Docker ................................................................................ 1 What is Docker?........................................................................................................................................................................2 Comparing Docker containers with virtual machines...........................................................................................3 A simple analogy.................................................................................................................................................................4 Docker terminology................................................................................................................................................................5 Docker containers, images, and registries .....................................................................................................................7 Choosing Between .NET 6 and .NET Framework for Docker Containers........................... 9 General guidance.....................................................................................................................................................................9 When to choose .NET for Docker containers............................................................................................................. 10 Developing and deploying cross platform ............................................................................................................ 10 Using containers for new (“green-field”) projects............................................................................................... 11 Create and deploy microservices on containers.................................................................................................. 11 Deploying high density in scalable systems.......................................................................................................... 11 When to choose .NET Framework for Docker containers..................................................................................... 12 Migrating existing applications directly to a Windows Server container .................................................. 12 Using third-party .NET libraries or NuGet packages not available for .NET 6 ......................................... 12 Using .NET technologies not available for .NET 6............................................................................................... 12 Using a platform or API that doesn’t support .NET 6........................................................................................ 13 Porting existing ASP.NET application to .NET 6................................................................................................... 13 Decision table: .NET frameworks to use for Docker................................................................................................ 13 What OS to target with .NET containers...................................................................................................................... 14 Official .NET Docker images ............................................................................................................................................. 16 .NET and Docker image optimizations for development versus production ........................................... 16 Architecting container and microservice-based applications .......................................... 18 Container design principles.............................................................................................................................................. 18 Containerizing monolithic applications ....................................................................................................................... 19 Deploying a monolithic application as a container............................................................................................ 21 Publishing a single-container-based application to Azure App Service.................................................... 21
  • 12. ii Contents Manage state and data in Docker applications........................................................................................................ 22 Service-oriented architecture........................................................................................................................................... 25 Microservices architecture................................................................................................................................................. 25 Additional resources ....................................................................................................................................................... 27 Data sovereignty per microservice ................................................................................................................................ 27 The relationship between microservices and the Bounded Context pattern........................................... 29 Logical architecture versus physical architecture..................................................................................................... 30 Challenges and solutions for distributed data management.............................................................................. 31 Challenge #1: How to define the boundaries of each microservice............................................................ 31 Challenge #2: How to create queries that retrieve data from several microservices............................ 32 Challenge #3: How to achieve consistency across multiple microservices............................................... 33 Challenge #4: How to design communication across microservice boundaries .................................... 35 Additional resources ....................................................................................................................................................... 36 Identify domain-model boundaries for each microservice.................................................................................. 36 The API gateway pattern versus the Direct client-to-microservice communication.................................. 40 Direct client-to-microservice communication ...................................................................................................... 40 Why consider API Gateways instead of direct client-to-microservice communication ....................... 41 What is the API Gateway pattern?............................................................................................................................. 42 Main features in the API Gateway pattern ............................................................................................................. 44 Using products with API Gateway features............................................................................................................ 45 Drawbacks of the API Gateway pattern................................................................................................................... 47 Additional resources ....................................................................................................................................................... 48 Communication in a microservice architecture ........................................................................................................ 48 Communication types .................................................................................................................................................... 49 Asynchronous microservice integration enforces microservice’s autonomy ........................................... 50 Communication styles.................................................................................................................................................... 52 Asynchronous message-based communication....................................................................................................... 54 Single-receiver message-based communication ................................................................................................ 55 Multiple-receivers message-based communication .......................................................................................... 56 Asynchronous event-driven communication........................................................................................................ 56 A note about messaging technologies for production systems ................................................................... 57 Resiliently publishing to the event bus ................................................................................................................... 58
  • 13. iii Contents Additional resources ....................................................................................................................................................... 58 Creating, evolving, and versioning microservice APIs and contracts............................................................... 59 Additional resources ....................................................................................................................................................... 59 Microservices addressability and the service registry ............................................................................................ 60 Additional resources ....................................................................................................................................................... 60 Creating composite UI based on microservices ....................................................................................................... 60 Additional resources ....................................................................................................................................................... 62 Resiliency and high availability in microservices...................................................................................................... 63 Health management and diagnostics in microservices .................................................................................... 63 Additional resources ....................................................................................................................................................... 65 Orchestrate microservices and multi-container applications for high scalability and availability ....... 66 Software platforms for container clustering, orchestration, and scheduling........................................... 68 Using container-based orchestrators in Microsoft Azure................................................................................ 68 Using Azure Kubernetes Service ................................................................................................................................ 68 Development environment for Kubernetes........................................................................................................... 69 Getting started with Azure Kubernetes Service (AKS) ....................................................................................... 70 Deploy with Helm charts into Kubernetes clusters............................................................................................. 70 Additional resources ....................................................................................................................................................... 71 Development process for Docker-based applications....................................................... 72 Development environment for Docker apps ............................................................................................................. 72 Development tool choices: IDE or editor................................................................................................................ 72 Additional resources ....................................................................................................................................................... 73 .NET languages and frameworks for Docker containers ....................................................................................... 73 Development workflow for Docker apps..................................................................................................................... 73 Workflow for developing Docker container-based applications .................................................................. 73 Step 1. Start coding and create your initial application or service baseline............................................. 75 Step 2. Create a Dockerfile related to an existing .NET base image............................................................ 76 Step 3. Create your custom Docker images and embed your application or service in them.......... 83 Step 4. Define your services in docker-compose.yml when building a multi-container Docker application .......................................................................................................................................................................... 84 Step 5. Build and run your Docker application .................................................................................................... 86 Step 6. Test your Docker application using your local Docker host............................................................ 89
  • 14. iv Contents Simplified workflow when developing containers with Visual Studio ........................................................ 90 Using PowerShell commands in a Dockerfile to set up Windows Containers......................................... 91 Designing and Developing Multi-Container and Microservice-Based .NET Applications ................................................................................................................................................. 93 Design a microservice-oriented application .............................................................................................................. 93 Application specifications............................................................................................................................................. 93 Development team context ......................................................................................................................................... 94 Choosing an architecture.............................................................................................................................................. 94 Benefits of a microservice-based solution ............................................................................................................. 97 Downsides of a microservice-based solution ....................................................................................................... 98 External versus internal architecture and design patterns............................................................................... 99 The new world: multiple architectural patterns and polyglot microservices..........................................100 Creating a simple data-driven CRUD microservice ...............................................................................................102 Designing a simple CRUD microservice................................................................................................................102 Implementing a simple CRUD microservice with ASP.NET Core.................................................................103 The DB connection string and environment variables used by Docker containers.............................109 Generating Swagger description metadata from your ASP.NET Core Web API ...................................111 Defining your multi-container application with docker-compose.yml .........................................................116 Use a database server running as a container ........................................................................................................127 SQL Server running as a container with a microservice-related database..............................................128 Seeding with test data on Web application startup.........................................................................................129 EF Core InMemory database versus SQL Server running as a container .................................................132 Using a Redis cache service running in a container.........................................................................................132 Implementing event-based communication between microservices (integration events)...................133 Using message brokers and services buses for production systems ........................................................134 Integration events..........................................................................................................................................................135 The event bus ..................................................................................................................................................................135 Additional resources .....................................................................................................................................................138 Implementing an event bus with RabbitMQ for the development or test environment.......................138 Implementing a simple publish method with RabbitMQ...............................................................................139 Implementing the subscription code with the RabbitMQ API.....................................................................140 Additional resources .....................................................................................................................................................141
  • 15. v Contents Subscribing to events........................................................................................................................................................141 Publishing events through the event bus.............................................................................................................142 Idempotency in update message events..............................................................................................................149 Deduplicating integration event messages .........................................................................................................150 Testing ASP.NET Core services and web apps ........................................................................................................152 Testing in eShopOnContainers.................................................................................................................................155 Implement background tasks in microservices with IHostedService and the BackgroundService class ....................................................................................................................................................................................................157 Registering hosted services in your WebHost or Host ...................................................................................158 The IHostedService interface.....................................................................................................................................159 Implementing IHostedService with a custom hosted service class deriving from the BackgroundService base class...................................................................................................................................160 Additional resources .....................................................................................................................................................163 Implement API Gateways with Ocelot ........................................................................................................................163 Architect and design your API Gateways..............................................................................................................163 Implementing your API Gateways with Ocelot ..................................................................................................168 Using Kubernetes Ingress plus Ocelot API Gateways......................................................................................180 Additional cross-cutting features in an Ocelot API Gateway .......................................................................181 Tackle Business Complexity in a Microservice with DDD and CQRS Patterns .............. 182 Apply simplified CQRS and DDD patterns in a microservice.............................................................................184 Additional resources .....................................................................................................................................................186 Apply CQRS and CQS approaches in a DDD microservice in eShopOnContainers .................................186 CQRS and DDD patterns are not top-level architectures...............................................................................187 Implement reads/queries in a CQRS microservice ................................................................................................188 Use ViewModels specifically made for client apps, independent from domain model constraints ...............................................................................................................................................................................................189 Use Dapper as a micro ORM to perform queries..............................................................................................189 Dynamic versus static ViewModels.........................................................................................................................190 Additional resources .....................................................................................................................................................193 Design a DDD-oriented microservice .........................................................................................................................194 Keep the microservice context boundaries relatively small ..........................................................................194 Layers in DDD microservices .....................................................................................................................................195
  • 16. vi Contents Design a microservice domain model ........................................................................................................................198 The Domain Entity pattern .........................................................................................................................................199 Implement a microservice domain model with .NET............................................................................................204 Domain model structure in a custom .NET Standard Library.......................................................................204 Structure aggregates in a custom .NET Standard library...............................................................................205 Implement domain entities as POCO classes .....................................................................................................206 Encapsulate data in the Domain Entities ..............................................................................................................207 Seedwork (reusable base classes and interfaces for your domain model)..................................................210 The custom Entity base class.....................................................................................................................................211 Repository contracts (interfaces) in the domain model layer ......................................................................212 Additional resources .....................................................................................................................................................213 Implement value objects..................................................................................................................................................213 Important characteristics of value objects ...........................................................................................................214 Value object implementation in C#........................................................................................................................215 How to persist value objects in the database with EF Core 2.0 and later................................................217 Persist value objects as owned entity types in EF Core 2.0 and later........................................................218 Additional resources .....................................................................................................................................................221 Use enumeration classes instead of enum types...................................................................................................222 Implement an Enumeration base class..................................................................................................................222 Additional resources .....................................................................................................................................................223 Design validations in the domain model layer .......................................................................................................223 Implement validations in the domain model layer...........................................................................................224 Additional resources .....................................................................................................................................................226 Client-side validation (validation in the presentation layers)............................................................................226 Additional resources .....................................................................................................................................................227 Domain events: design and implementation...........................................................................................................228 What is a domain event?.............................................................................................................................................228 Domain events versus integration events ............................................................................................................229 Domain events as a preferred way to trigger side effects across multiple aggregates within the same domain ...................................................................................................................................................................229 Implement domain events..........................................................................................................................................231 Conclusions on domain events.................................................................................................................................238
  • 17. vii Contents Additional resources .....................................................................................................................................................239 Design the infrastructure persistence layer..............................................................................................................239 The Repository pattern ................................................................................................................................................240 Additional resources .....................................................................................................................................................243 Implement the infrastructure persistence layer with Entity Framework Core ............................................244 Introduction to Entity Framework Core.................................................................................................................244 Infrastructure in Entity Framework Core from a DDD perspective.............................................................245 Implement custom repositories with Entity Framework Core......................................................................246 EF DbContext and IUnitOfWork instance lifetime in your IoC container.................................................249 The repository instance lifetime in your IoC container...................................................................................249 Table mapping ................................................................................................................................................................250 Implement the Query Specification pattern........................................................................................................253 Use NoSQL databases as a persistence infrastructure.........................................................................................255 Introduction to Azure Cosmos DB and the native Cosmos DB API ...........................................................256 Implement .NET code targeting MongoDB and Azure Cosmos DB ..........................................................258 Design the microservice application layer and Web API ....................................................................................266 Use SOLID principles and Dependency Injection..............................................................................................266 Implement the microservice application layer using the Web API.................................................................267 Use Dependency Injection to inject infrastructure objects into your application layer.....................267 Implement the Command and Command Handler patterns .......................................................................271 The Command process pipeline: how to trigger a command handler.....................................................278 Implement the command process pipeline with a mediator pattern (MediatR) ..................................281 Apply cross-cutting concerns when processing commands with the Behaviors in MediatR ..........287 Implement resilient applications ....................................................................................... 291 Handle partial failure.........................................................................................................................................................292 Strategies to handle partial failure...............................................................................................................................294 Additional resources .....................................................................................................................................................295 Implement retries with exponential backoff ............................................................................................................295 Implement resilient Entity Framework Core SQL connections..........................................................................295 Execution strategies and explicit transactions using BeginTransaction and multiple DbContexts296 Additional resources .....................................................................................................................................................298 Use IHttpClientFactory to implement resilient HTTP requests .........................................................................298
  • 18. viii Contents Issues with the original HttpClient class available in .NET.............................................................................298 Benefits of using IHttpClientFactory.......................................................................................................................299 Multiple ways to use IHttpClientFactory...............................................................................................................300 How to use Typed Clients with IHttpClientFactory...........................................................................................300 Additional resources .....................................................................................................................................................303 Implement HTTP call retries with exponential backoff with IHttpClientFactory and Polly policies ...304 Add a jitter strategy to the retry policy.................................................................................................................305 Additional resources .....................................................................................................................................................305 Implement the Circuit Breaker pattern.......................................................................................................................305 Implement Circuit Breaker pattern with IHttpClientFactory and Polly .....................................................306 Test Http retries and circuit breakers in eShopOnContainers......................................................................307 Additional resources .....................................................................................................................................................309 Health monitoring ..............................................................................................................................................................309 Implement health checks in ASP.NET Core services ........................................................................................310 Use watchdogs................................................................................................................................................................314 Health checks when using orchestrators..............................................................................................................316 Advanced monitoring: visualization, analysis, and alerts ...............................................................................316 Additional resources .....................................................................................................................................................317 Make secure .NET Microservices and Web Applications................................................. 318 Implement authentication in .NET microservices and web applications ......................................................318 Authenticate with ASP.NET Core Identity.............................................................................................................319 Authenticate with external providers.....................................................................................................................321 Authenticate with bearer tokens..............................................................................................................................322 Authenticate with an OpenID Connect or OAuth 2.0 Identity provider...................................................323 Issue security tokens from an ASP.NET Core service.......................................................................................324 Consume security tokens............................................................................................................................................325 Additional resources..........................................................................................................................................................326 About authorization in .NET microservices and web applications..................................................................327 Implement role-based authorization .....................................................................................................................327 Implement policy-based authorization .................................................................................................................328 Additional resources .....................................................................................................................................................329 Store application secrets safely during development..........................................................................................329
  • 19. ix Contents Store secrets in environment variables .................................................................................................................330 Store secrets with the ASP.NET Core Secret Manager ....................................................................................330 Use Azure Key Vault to protect secrets at production time ..............................................................................331 Additional resources .....................................................................................................................................................332 .NET Microservices Architecture key takeaways.............................................................. 333
  • 20. 1 CHAPTER 1 | Introduction to Containers and Docker CHAPTER 1 Introduction to Containers and Docker Containerization is an approach to software development in which an application or service, its dependencies, and its configuration (abstracted as deployment manifest files) are packaged together as a container image. The containerized application can be tested as a unit and deployed as a container image instance to the host operating system (OS). Just as shipping containers allow goods to be transported by ship, train, or truck regardless of the cargo inside, software containers act as a standard unit of software deployment that can contain different code and dependencies. Containerizing software this way enables developers and IT professionals to deploy them across environments with little or no modification. Containers also isolate applications from each other on a shared OS. Containerized applications run on top of a container host that in turn runs on the OS (Linux or Windows). Containers therefore have a significantly smaller footprint than virtual machine (VM) images. Each container can run a whole web application or a service, as shown in Figure 2-1. In this example, Docker host is a container host, and App1, App2, Svc 1, and Svc 2 are containerized applications or services. Figure 2-1. Multiple containers running on a container host
  • 21. 2 CHAPTER 1 | Introduction to Containers and Docker Another benefit of containerization is scalability. You can scale out quickly by creating new containers for short-term tasks. From an application point of view, instantiating an image (creating a container) is similar to instantiating a process like a service or a web app. For reliability, however, when you run multiple instances of the same image across multiple host servers, you typically want each container (image instance) to run in a different host server or VM in different fault domains. In short, containers offer the benefits of isolation, portability, agility, scalability, and control across the whole application lifecycle workflow. The most important benefit is the environment’s isolation provided between Dev and Ops. What is Docker? Docker is an open-source project for automating the deployment of applications as portable, self- sufficient containers that can run on the cloud or on-premises. Docker is also a company that promotes and evolves this technology, working in collaboration with cloud, Linux, and Windows vendors, including Microsoft. Figure 2-2. Docker deploys containers at all layers of the hybrid cloud. Docker containers can run anywhere, on-premises in the customer datacenter, in an external service provider or in the cloud, on Azure. Docker image containers can run natively on Linux and Windows. However, Windows images can run only on Windows hosts and Linux images can run on Linux hosts and Windows hosts (using a Hyper-V Linux VM, so far), where host means a server or a VM. Developers can use development environments on Windows, Linux, or macOS. On the development computer, the developer runs a Docker host where Docker images are deployed, including the app and its dependencies. Developers who work on Linux or on macOS use a Docker host that is Linux based, and they can create images only for Linux containers. (Developers working on macOS can edit code or run the Docker CLI from macOS, but as of the time of this writing, containers don’t run
  • 22. 3 CHAPTER 1 | Introduction to Containers and Docker directly on macOS.) Developers who work on Windows can create images for either Linux or Windows Containers. To host containers in development environments and provide additional developer tools, Docker ships Docker Desktop for Windows or for macOS. These products install the necessary VM (the Docker host) to host the containers. To run Windows Containers, there are two types of runtimes: • Windows Server Containers provide application isolation through process and namespace isolation technology. A Windows Server Container shares a kernel with the container host and with all containers running on the host. • Hyper-V Containers expand on the isolation provided by Windows Server Containers by running each container in a highly optimized virtual machine. In this configuration, the kernel of the container host isn’t shared with the Hyper-V Containers, providing better isolation. The images for these containers are created the same way and function the same. The difference is in how the container is created from the image running a Hyper-V Container requires an extra parameter. For details, see Hyper-V Containers. Comparing Docker containers with virtual machines Figure 2-3 shows a comparison between VMs and Docker containers. Virtual Machines Docker Containers
  • 23. 4 CHAPTER 1 | Introduction to Containers and Docker Virtual Machines Docker Containers Virtual machines include the application, the required libraries or binaries, and a full guest operating system. Full virtualization requires more resources than containerization. Containers include the application and all its dependencies. However, they share the OS kernel with other containers, running as isolated processes in user space on the host operating system. (Except in Hyper-V containers, where each container runs inside of a special virtual machine per container.) Figure 2-3. Comparison of traditional virtual machines to Docker containers For VMs, there are three base layers in the host server, from the bottom-up: infrastructure, Host Operating System and a Hypervisor and on top of all that each VM has its own OS and all necessary libraries. For Docker, the host server only has the infrastructure and the OS and on top of that, the container engine, that keeps container isolated but sharing the base OS services. Because containers require far fewer resources (for example, they don’t need a full OS), they’re easy to deploy and they start fast. This allows you to have higher density, meaning that it allows you to run more services on the same hardware unit, thereby reducing costs. As a side effect of running on the same kernel, you get less isolation than VMs. The main goal of an image is that it makes the environment (dependencies) the same across different deployments. This means that you can debug it on your machine and then deploy it to another machine with the same environment guaranteed. A container image is a way to package an app or service and deploy it in a reliable and reproducible way. You could say that Docker isn’t only a technology but also a philosophy and a process. When using Docker, you won’t hear developers say, “It works on my machine, why not in production?” They can simply say, “It runs on Docker”, because the packaged Docker application can be executed on any supported Docker environment, and it runs the way it was intended to on all deployment targets (such as Dev, QA, staging, and production). A simple analogy Perhaps a simple analogy can help getting the grasp of the core concept of Docker. Let’s go back in time to the 1950s for a moment. There were no word processors, and the photocopiers were used everywhere (kind of). Imagine you’re responsible for quickly issuing batches of letters as required, to mail them to customers, using real paper and envelopes, to be delivered physically to each customer’s address (there was no email back then). At some point, you realize the letters are just a composition of a large set of paragraphs, which are picked and arranged as needed, according to the purpose of the letter, so you devise a system to issue letters quickly, expecting to get a hefty raise. The system is simple: 1. You begin with a deck of transparent sheets containing one paragraph each.
  • 24. 5 CHAPTER 1 | Introduction to Containers and Docker 2. To issue a set of letters, you pick the sheets with the paragraphs you need, then you stack and align them so they look and read fine. 3. Finally, you place the set in the photocopier and press start to produce as many letters as required. So, simplifying, that’s the core idea of Docker. In Docker, each layer is the resulting set of changes that happen to the filesystem after executing a command, such as, installing a program. So, when you “look” at the filesystem after the layer has been copied, you see all the files, included in the layer when the program was installed. You can think of an image as an auxiliary read-only hard disk ready to be installed in a “computer” where the operating system is already installed. Similarly, you can think of a container as the “computer” with the image hard disk installed. The container, just like a computer, can be powered on or off. Docker terminology This section lists terms and definitions you should be familiar with before getting deeper into Docker. For further definitions, see the extensive glossary provided by Docker. Container image: A package with all the dependencies and information needed to create a container. An image includes all the dependencies (such as frameworks) plus deployment and execution configuration to be used by a container runtime. Usually, an image derives from multiple base images that are layers stacked on top of each other to form the container’s filesystem. An image is immutable once it has been created. Dockerfile: A text file that contains instructions for building a Docker image. It’s like a batch script, the first line states the base image to begin with and then follow the instructions to install required programs, copy files, and so on, until you get the working environment you need. Build: The action of building a container image based on the information and context provided by its Dockerfile, plus additional files in the folder where the image is built. You can build images with the following Docker command: docker build Container: An instance of a Docker image. A container represents the execution of a single application, process, or service. It consists of the contents of a Docker image, an execution environment, and a standard set of instructions. When scaling a service, you create multiple instances of a container from the same image. Or a batch job can create multiple containers from the same image, passing different parameters to each instance. Volumes: Offer a writable filesystem that the container can use. Since images are read-only but most programs need to write to the filesystem, volumes add a writable layer, on top of the container image, so the programs have access to a writable filesystem. The program doesn’t know it’s accessing a
  • 25. 6 CHAPTER 1 | Introduction to Containers and Docker layered filesystem, it’s just the filesystem as usual. Volumes live in the host system and are managed by Docker. Tag: A mark or label you can apply to images so that different images or versions of the same image (depending on the version number or the target environment) can be identified. Multi-stage Build: Is a feature, since Docker 17.05 or higher, that helps to reduce the size of the final images. For example, a large base image, containing the SDK can be used for compiling and publishing and then a small runtime-only base image can be used to host the application. Repository (repo): A collection of related Docker images, labeled with a tag that indicates the image version. Some repos contain multiple variants of a specific image, such as an image containing SDKs (heavier), an image containing only runtimes (lighter), etc. Those variants can be marked with tags. A single repo can contain platform variants, such as a Linux image and a Windows image. Registry: A service that provides access to repositories. The default registry for most public images is Docker Hub (owned by Docker as an organization). A registry usually contains repositories from multiple teams. Companies often have private registries to store and manage images they’ve created. Azure Container Registry is another example. Multi-arch image: For multi-architecture, it’s a feature that simplifies the selection of the appropriate image, according to the platform where Docker is running. For example, when a Dockerfile requests a base image FROM mcr.microsoft.com/dotnet/sdk:6.0 from the registry, it actually gets 6.0- nanoserver-20H2, 6.0-nanoserver-1809 or 6.0-bullseye-slim, depending on the operating system and version where Docker is running. Docker Hub: A public registry to upload images and work with them. Docker Hub provides Docker image hosting, public or private registries, build triggers and web hooks, and integration with GitHub and Bitbucket. Azure Container Registry: A public resource for working with Docker images and its components in Azure. This provides a registry that’s close to your deployments in Azure and that gives you control over access, making it possible to use your Azure Active Directory groups and permissions. Docker Trusted Registry (DTR): A Docker registry service (from Docker) that can be installed on- premises so it lives within the organization’s datacenter and network. It’s convenient for private images that should be managed within the enterprise. Docker Trusted Registry is included as part of the Docker Datacenter product. Docker Desktop: Development tools for Windows and macOS for building, running, and testing containers locally. Docker Desktop for Windows provides development environments for both Linux and Windows Containers. The Linux Docker host on Windows is based on a Hyper-V virtual machine. The host for Windows Containers is directly based on Windows. Docker Desktop for Mac is based on the Apple Hypervisor framework and the xhyve hypervisor, which provides a Linux Docker host virtual machine on macOS. Docker Desktop for Windows and for Mac replaces Docker Toolbox, which was based on Oracle VirtualBox. Compose: A command-line tool and YAML file format with metadata for defining and running multi- container applications. You define a single application based on multiple images with one or more .yml files that can override values depending on the environment. After you’ve created the definitions,
  • 26. 7 CHAPTER 1 | Introduction to Containers and Docker you can deploy the whole multi-container application with a single command (docker-compose up) that creates a container per image on the Docker host. Cluster: A collection of Docker hosts exposed as if it were a single virtual Docker host, so that the application can scale to multiple instances of the services spread across multiple hosts within the cluster. Docker clusters can be created with Kubernetes, Azure Service Fabric, Docker Swarm and Mesosphere DC/OS. Orchestrator: A tool that simplifies the management of clusters and Docker hosts. Orchestrators enable you to manage their images, containers, and hosts through a command-line interface (CLI) or a graphical UI. You can manage container networking, configurations, load balancing, service discovery, high availability, Docker host configuration, and more. An orchestrator is responsible for running, distributing, scaling, and healing workloads across a collection of nodes. Typically, orchestrator products are the same products that provide cluster infrastructure, like Kubernetes and Azure Service Fabric, among other offerings in the market. Docker containers, images, and registries When using Docker, a developer creates an app or service and packages it and its dependencies into a container image. An image is a static representation of the app or service and its configuration and dependencies. To run the app or service, the app’s image is instantiated to create a container, which will be running on the Docker host. Containers are initially tested in a development environment or PC. Developers should store images in a registry, which acts as a library of images and is needed when deploying to production orchestrators. Docker maintains a public registry via Docker Hub; other vendors provide registries for different collections of images, including Azure Container Registry. Alternatively, enterprises can have a private registry on-premises for their own Docker images. Figure 2-4 shows how images and registries in Docker relate to other components. It also shows the multiple registry offerings from vendors.
  • 27. 8 CHAPTER 1 | Introduction to Containers and Docker Figure 2-4. Taxonomy of Docker terms and concepts The registry is like a bookshelf where images are stored and available to be pulled for building containers to run services or web apps. There are private Docker registries on-premises and on the public cloud. Docker Hub is a public registry maintained by Docker, along the Docker Trusted Registry an enterprise-grade solution, Azure offers the Azure Container Registry. AWS, Google, and others also have container registries. Putting images in a registry lets you store static and immutable application bits, including all their dependencies at a framework level. Those images can then be versioned and deployed in multiple environments and therefore provide a consistent deployment unit. Private image registries, either hosted on-premises or in the cloud, are recommended when: • Your images must not be shared publicly due to confidentiality. • You want to have minimum network latency between your images and your chosen deployment environment. For example, if your production environment is Azure cloud, you probably want to store your images in Azure Container Registry so that network latency will be minimal. In a similar way, if your production environment is on-premises, you might want to have an on- premises Docker Trusted Registry available within the same local network.
  • 28. 9 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers CHAPTER 2 Choosing Between .NET 6 and .NET Framework for Docker Containers There are two supported frameworks for building server-side containerized Docker applications with .NET: .NET Framework and .NET 6. They share many .NET platform components, and you can share code across the two. However, there are fundamental differences between them, and which framework you use will depend on what you want to accomplish. This section provides guidance on when to choose each framework. General guidance This section provides a summary of when to choose .NET 6 or .NET Framework. We provide more details about these choices in the sections that follow. Use .NET 6, with Linux or Windows Containers, for your containerized Docker server application when: • You have cross-platform needs. For example, you want to use both Linux and Windows Containers. • Your application architecture is based on microservices. • You need to start containers fast and want a small footprint per container to achieve better density or more containers per hardware unit in order to lower your costs. In short, when you create new containerized .NET applications, you should consider .NET 6 as the default choice. It has many benefits and fits best with the containers philosophy and style of working. An extra benefit of using .NET 6 is that you can run side-by-side .NET versions for applications within the same machine. This benefit is more important for servers or VMs that do not use containers, because containers isolate the versions of .NET that the app needs. (As long as they are compatible with the underlying OS.) Use .NET Framework for your containerized Docker server application when: • Your application currently uses .NET Framework and has strong dependencies on Windows.
  • 29. 10 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers • You need to use Windows APIs that are not supported by .NET 6. • You need to use third-party .NET libraries or NuGet packages that are not available for .NET 6. Using .NET Framework on Docker can improve your deployment experiences by minimizing deployment issues. This “lift and shift” scenario is important for containerizing legacy applications that were originally developed with the traditional .NET Framework, like ASP.NET WebForms, MVC web apps or WCF (Windows Communication Foundation) services. Additional resources • E-book: Modernize existing .NET Framework applications with Azure and Windows Containers https://aka.ms/liftandshiftwithcontainersebook • Sample apps: Modernization of legacy ASP.NET web apps by using Windows Containers https://aka.ms/eshopmodernizing When to choose .NET for Docker containers The modularity and lightweight nature of .NET 6 makes it perfect for containers. When you deploy and start a container, its image is far smaller with .NET 6 than with .NET Framework. In contrast, to use .NET Framework for a container, you must base your image on the Windows Server Core image, which is a lot heavier than the Windows Nano Server or Linux images that you use for .NET 6. Additionally, .NET 6 is cross-platform, so you can deploy server apps with Linux or Windows container images. However, if you are using the traditional .NET Framework, you can only deploy images based on Windows Server Core. The following is a more detailed explanation of why to choose .NET 6. Developing and deploying cross platform Clearly, if your goal is to have an application (web app or service) that can run on multiple platforms supported by Docker (Linux and Windows), the right choice is .NET 6, because .NET Framework only supports Windows. .NET 6 also supports macOS as a development platform. However, when you deploy containers to a Docker host, that host must (currently) be based on Linux or Windows. For example, in a development environment, you could use a Linux VM running on a Mac. Visual Studio provides an integrated development environment (IDE) for Windows and supports Docker development. Visual Studio for Mac is an IDE, evolution of Xamarin Studio, that runs on macOS and supports Docker-based application development. This tool should be the preferred choice for developers working in Mac machines who also want to use a powerful IDE. You can also use Visual Studio Code on macOS, Linux, and Windows. Visual Studio Code fully supports .NET 6, including IntelliSense and debugging. Because VS Code is a lightweight editor, you
  • 30. 11 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers can use it to develop containerized apps on the machine in conjunction with the Docker CLI and the .NET CLI. You can also target .NET 6 with most third-party editors like Sublime, Emacs, vi, and the open-source OmniSharp project, which also provides IntelliSense support. In addition to the IDEs and editors, you can use the .NET CLI for all supported platforms. Using containers for new (“green-field”) projects Containers are commonly used in conjunction with a microservices architecture, although they can also be used to containerize web apps or services that follow any architectural pattern. You can use .NET Framework on Windows Containers, but the modularity and lightweight nature of .NET 6 makes it perfect for containers and microservices architectures. When you create and deploy a container, its image is far smaller with .NET 6 than with .NET Framework. Create and deploy microservices on containers You could use the traditional .NET Framework for building microservices-based applications (without containers) by using plain processes. That way, because the .NET Framework is already installed and shared across processes, processes are light and fast to start. However, if you are using containers, the image for the traditional .NET Framework is also based on Windows Server Core and that makes it too heavy for a microservices-on-containers approach. However, teams have been looking for opportunities to improve the experience for .NET Framework users as well. Recently, size of the Windows Server Core container images have been reduced to >40% smaller. On the other hand, .NET 6 is the best candidate if you’re embracing a microservices-oriented system that is based on containers because .NET 6 is lightweight. In addition, its related container images, for either Linux or Windows Nano Server, are lean and small, making containers light and fast to start. A microservice is meant to be as small as possible: to be light when spinning up, to have a small footprint, to have a small Bounded Context (check DDD, Domain-Driven Design), to represent a small area of concerns, and to be able to start and stop fast. For those requirements, you will want to use small and fast-to-instantiate container images like the .NET 6 container image. A microservices architecture also allows you to mix technologies across a service boundary. This approach enables a gradual migration to .NET 6 for new microservices that work in conjunction with other microservices or with services developed with Node.js, Python, Java, GoLang, or other technologies. Deploying high density in scalable systems When your container-based system needs the best possible density, granularity, and performance, .NET and ASP.NET Core are your best options. ASP.NET Core is up to 10 times faster than ASP.NET in the traditional .NET Framework, and it leads to other popular industry technologies for microservices, such as Java servlets, Go, and Node.js. This approach is especially relevant for microservices architectures, where you could have hundreds of microservices (containers) running. With ASP.NET Core images (based on the .NET runtime) on Linux or Windows Nano, you can run your system with a much lower number of servers or VMs, ultimately saving costs in infrastructure and hosting.
  • 31. 12 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers When to choose .NET Framework for Docker containers While .NET 6 offers significant benefits for new applications and application patterns, .NET Framework will continue to be a good choice for many existing scenarios. Migrating existing applications directly to a Windows Server container You might want to use Docker containers just to simplify deployment, even if you are not creating microservices. For example, perhaps you want to improve your DevOps workflow with Docker— containers can give you better isolated test environments and can also eliminate deployment issues caused by missing dependencies when you move to a production environment. In cases like these, even if you are deploying a monolithic application, it makes sense to use Docker and Windows Containers for your current .NET Framework applications. In most cases for this scenario, you will not need to migrate your existing applications to .NET 6; you can use Docker containers that include the traditional .NET Framework. However, a recommended approach is to use .NET 6 as you extend an existing application, such as writing a new service in ASP.NET Core. Using third-party .NET libraries or NuGet packages not available for .NET 6 Third-party libraries are quickly embracing .NET Standard, which enables code sharing across all .NET flavors, including .NET 6. With .NET Standard 2.0 and later, the API surface compatibility across different frameworks has become significantly larger. Even more, .NET Core 2.x and newer applications can also directly reference existing .NET Framework libraries (see .NET Framework 4.6.1 supporting .NET Standard 2.0). In addition, the Windows Compatibility Pack extends the API surface available for .NET Standard 2.0 on Windows. This pack allows recompiling most existing code to .NET Standard 2.x with little or no modification, to run on Windows. However, even with that exceptional progression since .NET Standard 2.0 and .NET Core 2.1 or later, there might be cases where certain NuGet packages need Windows to run and might not support .NET Core or later. If those packages are critical for your application, then you will need to use .NET Framework on Windows Containers. Using .NET technologies not available for .NET 6 Some .NET Framework technologies aren’t available in the current version of .NET (version 5.0 as of this writing). Some of them might become available in later releases, but others don’t fit the new application patterns targeted by .NET Core and might never be available. The following list shows most of the technologies that aren’t available in .NET 6:
  • 32. 13 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers • ASP.NET Web Forms. This technology is only available on .NET Framework. Currently there are no plans to bring ASP.NET Web Forms to .NET or later. • WCF services. Even when a WCF-Client library is available to consume WCF services from .NET 6, as of Jan-2021, the WCF server implementation is only available on .NET Framework. • Workflow-related services. Windows Workflow Foundation (WF), Workflow Services (WCF + WF in a single service), and WCF Data Services (formerly known as ADO.NET Data Services) are only available on .NET Framework. There are currently no plans to bring them to .NET 6. In addition to the technologies listed in the official .NET roadmap, other features might be ported to the new unified .NET platform. You might consider participating in the discussions on GitHub so that your voice can be heard. And if you think something is missing, file a new issue in the dotnet/runtime GitHub repository. Using a platform or API that doesn’t support .NET 6 Some Microsoft and third-party platforms don’t support .NET 6. For example, some Azure services provide an SDK that isn’t yet available for consumption on .NET 6 yet. Most Azure SDK should eventually be ported to .NET 6/.NET Standard, but some might not for several reasons. You can see the available Azure SDKs in the Azure SDK Latest Releases page. In the meantime, if any platform or service in Azure still doesn’t support .NET 6 with its client API, you can use the equivalent REST API from the Azure service or the client SDK on .NET Framework. Porting existing ASP.NET application to .NET 6 .NET Core is a revolutionary step forward from .NET Framework. It offers a host of advantages over .NET Framework across the board from productivity to performance, and from cross-platform support to developer satisfaction. If you are using .NET Framework and planning to migrate your application to .NET Core or .NET 5+, see Porting Existing ASP.NET Apps to .NET Core. Additional resources • .NET fundamentals https://docs.microsoft.com/dotnet/fundamentals • Porting Projects to .NET 5 https://docs.microsoft.com/events/dotnetconf-2020/porting-projects-to-net-5 • .NET on Docker Guide https://docs.microsoft.com/dotnet/core/docker/introduction Decision table: .NET frameworks to use for Docker The following decision table summarizes whether to use .NET Framework or .NET 6. Remember that for Linux containers, you need Linux-based Docker hosts (VMs or servers) and that for Windows Containers you need Windows Server based Docker hosts (VMs or servers).
  • 33. 14 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers Important Your development machines will run one Docker host, either Linux or Windows. Related microservices that you want to run and test together in one solution will all need to run on the same container platform. Architecture / App Type Linux containers Windows Containers Microservices on containers .NET 6 .NET 6 Monolithic app .NET 6 .NET Framework .NET 6 Best-in-class performance and scalability .NET 6 .NET 6 Windows Server legacy app (“brown-field”) migration to containers – .NET Framework New container-based development (“green-field”) .NET 6 .NET 6 ASP.NET Core .NET 6 .NET 6 (recommended) .NET Framework ASP.NET 4 (MVC 5, Web API 2, and Web Forms) – .NET Framework SignalR services .NET Core 2.1 or higher version .NET Framework .NET Core 2.1 or higher version WCF, WF, and other legacy frameworks WCF in .NET Core (client library only) .NET Framework WCF in .NET 6 (client library only) Consumption of Azure services .NET 6 (eventually most Azure services will provide client SDKs for .NET 6) .NET Framework .NET 6 (eventually most Azure services will provide client SDKs for .NET 6) What OS to target with .NET containers Given the diversity of operating systems supported by Docker and the differences between .NET Framework and .NET 6, you should target a specific OS and specific versions depending on the framework you are using. For Windows, you can use Windows Server Core or Windows Nano Server. These Windows versions provide different characteristics (IIS in Windows Server Core versus a self-hosted web server like Kestrel in Nano Server) that might be needed by .NET Framework or .NET 6, respectively. For Linux, multiple distros are available and supported in official .NET Docker images (like Debian). In Figure 3-1, you can see the possible OS version depending on the .NET framework used.
  • 34. 15 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers Figure 3-1. Operating systems to target depending on versions of the .NET framework When deploying legacy .NET Framework applications you have to target Windows Server Core, compatible with legacy apps and IIS, but it has a larger image. When deploying .NET 6 applications, you can target Windows Nano Server, which is cloud optimized, uses Kestrel and is smaller and starts faster. You can also target Linux, supporting Debian, Alpine, and others. Also uses Kestrel, is smaller, and starts faster. You can also create your own Docker image in cases where you want to use a different Linux distro or where you want an image with versions not provided by Microsoft. For example, you might create an image with ASP.NET Core running on the traditional .NET Framework and Windows Server Core, which is a not-so-common scenario for Docker. When you add the image name to your Dockerfile file, you can select the operating system and version depending on the tag you use, as in the following examples: Image Comments mcr.microsoft.com/dotnet/runtime:6.0 .NET 6 multi-architecture: Supports Linux and Windows Nano Server depending on the Docker host. mcr.microsoft.com/dotnet/aspnet:6.0 ASP.NET Core 6.0 multi-architecture: Supports Linux and Windows Nano Server depending on the Docker host. The aspnetcore image has a few optimizations for ASP.NET Core. mcr.microsoft.com/dotnet/aspnet:6.0- bullseye-slim .NET 6 runtime-only on Linux Debian distro
  • 35. 16 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers Image Comments mcr.microsoft.com/dotnet/aspnet:6.0- nanoserver-1809 .NET 6 runtime-only on Windows Nano Server (Windows Server version 1809) Official .NET Docker images The Official .NET Docker images are Docker images created and optimized by Microsoft. They are publicly available in the Microsoft repositories on Docker Hub. Each repository can contain multiple images, depending on .NET versions, and depending on the OS and versions (Linux Debian, Linux Alpine, Windows Nano Server, Windows Server Core, etc.). Since .NET Core 2.1, all the .NET Core or later images, including for ASP.NET Core are available at Docker Hub at the .NET image repository: https://hub.docker.com/_/microsoft-dotnet/. Since May 2018, Microsoft images are being syndicated in the Microsoft Container Registry. The official catalog is still only available in Docker Hub, and there you’ll find the updated address to pull the image. Most image repositories provide extensive tagging to help you select not just a specific framework version, but also to choose an OS (Linux distribution or Windows version). .NET and Docker image optimizations for development versus production When building Docker images for developers, Microsoft focused on the following main scenarios: • Images used to develop and build .NET apps. • Images used to run .NET apps. Why multiple images? When developing, building, and running containerized applications, you usually have different priorities. By providing different images for these separate tasks, Microsoft helps optimize the separate processes of developing, building, and deploying apps. During development and build During development, what is important is how fast you can iterate changes, and the ability to debug the changes. The size of the image isn’t as important as the ability to make changes to your code and see the changes quickly. Some tools and “build-agent containers”, use the development .NET image (mcr.microsoft.com/dotnet/sdk:6.0) during development and build process. When building inside a Docker container, the important aspects are the elements that are needed to compile your app. This includes the compiler and any other .NET dependencies. Why is this type of build image important? You don’t deploy this image to production. Instead, it’s an image that you use to build the content you place into a production image. This image would be used in your continuous integration (CI) environment or build environment when using Docker multi-stage builds.
  • 36. 17 CHAPTER 2 | Choosing Between .NET 6 and .NET Framework for Docker Containers In production What is important in production is how fast you can deploy and start your containers based on a production .NET image. Therefore, the runtime-only image based on mcr.microsoft.com/dotnet/aspnet:6.0 is small so that it can travel quickly across the network from your Docker registry to your Docker hosts. The contents are ready to run, enabling the fastest time from starting the container to processing results. In the Docker model, there is no need for compilation from C# code, as there is when you run dotnet build or dotnet publish when using the build container. In this optimized image, you put only the binaries and other content needed to run the application. For example, the content created by dotnet publish contains only the compiled .NET binaries, images, .js, and .css files. Over time, you will see images that contain pre-jitted (the compilation from IL to native that occurs at run time) packages. Although there are multiple versions of the .NET and ASP.NET Core images, they all share one or more layers, including the base layer. Therefore, the amount of disk space needed to store an image is small; it consists only of the delta between your custom image and its base image. The result is that it is quick to pull the image from your registry. When you explore the .NET image repositories at Docker Hub, you will find multiple image versions classified or marked with tags. These tags help to decide which one to use, depending on the version you need, like those in the following table: Image Comments mcr.microsoft.com/dotnet/aspnet:6.0 ASP.NET Core, with runtime only and ASP.NET Core optimizations, on Linux and Windows (multi-arch) mcr.microsoft.com/dotnet/sdk:6.0 .NET 6, with SDKs included, on Linux and Windows (multi- arch) You can find all the available docker images in dotnet-docker and also refer to the latest preview releases by using nightly build mcr.microsoft.com/dotnet/nightly/*
  • 37. 18 CHAPTER 3 | Architecting container and microservice-based applications CHAPTER 3 Architecting container and microservice-based applications Microservices offer great benefits but also raise huge new challenges. Microservice architecture patterns are fundamental pillars when creating a microservice-based application. Earlier in this guide, you learned basic concepts about containers and Docker. That information was the minimum you needed to get started with containers. Even though containers are enablers of, and a great fit for microservices, they aren’t mandatory for a microservice architecture. Many architectural concepts in this architecture section could be applied without containers. However, this guide focuses on the intersection of both due to the already introduced importance of containers. Enterprise applications can be complex and are often composed of multiple services instead of a single service-based application. For those cases, you need to understand other architectural approaches, such as the microservices and certain Domain-Driven Design (DDD) patterns plus container orchestration concepts. Note that this chapter describes not just microservices on containers, but any containerized application, as well. Container design principles In the container model, a container image instance represents a single process. By defining a container image as a process boundary, you can create primitives that can be used to scale or batch the process. When you design a container image, you’ll see an ENTRYPOINT definition in the Dockerfile. This definition defines the process whose lifetime controls the lifetime of the container. When the process completes, the container lifecycle ends. Containers might represent long-running processes like web servers, but can also represent short-lived processes like batch jobs, which formerly might have been implemented as Azure WebJobs. If the process fails, the container ends, and the orchestrator takes over. If the orchestrator was configured to keep five instances running and one fails, the orchestrator will create another container instance to replace the failed process. In a batch job, the process is started with parameters. When the process completes, the work is complete. This guidance drills-down on orchestrators, later on.
  • 38. 19 CHAPTER 3 | Architecting container and microservice-based applications You might find a scenario where you want multiple processes running in a single container. For that scenario, since there can be only one entry point per container, you could run a script within the container that launches as many programs as needed. For example, you can use Supervisor or a similar tool to take care of launching multiple processes inside a single container. However, even though you can find architectures that hold multiple processes per container, that approach isn’t very common. Containerizing monolithic applications You might want to build a single, monolithically deployed web application or service and deploy it as a container. The application itself might not be internally monolithic, but structured as several libraries, components, or even layers (application layer, domain layer, data-access layer, etc.). Externally, however, it’s a single container—a single process, a single web application, or a single service. To manage this model, you deploy a single container to represent the application. To increase capacity, you scale out, that is, just add more copies with a load balancer in front. The simplicity comes from managing a single deployment in a single container or VM. Figure 4-1. Example of the architecture of a containerized monolithic application You can include multiple components, libraries, or internal layers in each container, as illustrated in Figure 4-1. A monolithic containerized application has most of its functionality within a single container, with internal layers or libraries, and scales out by cloning the container on multiple servers/VMs. However, this monolithic pattern might conflict with the container principle “a container does one thing, and does it in one process”, but might be ok for some cases. The downside of this approach becomes evident if the application grows, requiring it to scale. If the entire application can scale, it isn’t really a problem. However, in most cases, just a few parts of the application are the choke points that require scaling, while other components are used less. For example, in a typical e-commerce application, you likely need to scale the product information subsystem, because many more customers browse products than purchase them. More customers use
  • 39. 20 CHAPTER 3 | Architecting container and microservice-based applications their basket than use the payment pipeline. Fewer customers add comments or view their purchase history. And you might have only a handful of employees that need to manage the content and marketing campaigns. If you scale the monolithic design, all the code for these different tasks is deployed multiple times and scaled at the same grade. There are multiple ways to scale an application-horizontal duplication, splitting different areas of the application, and partitioning similar business concepts or data. But, in addition to the problem of scaling all components, changes to a single component require complete retesting of the entire application, and a complete redeployment of all the instances. However, the monolithic approach is common, because the development of the application is initially easier than for microservices approaches. Thus, many organizations develop using this architectural approach. While some organizations have had good enough results, others are hitting limits. Many organizations designed their applications using this model because tools and infrastructure made it too difficult to build service-oriented architectures (SOA) years ago, and they did not see the need- until the application grew. From an infrastructure perspective, each server can run many applications within the same host and have an acceptable ratio of efficiency in resources usage, as shown in Figure 4-2. Figure 4-2. Monolithic approach: Host running multiple apps, each app running as a container Monolithic applications in Microsoft Azure can be deployed using dedicated VMs for each instance. Additionally, using Azure virtual machine scale sets, you can easily scale the VMs. Azure App Service can also run monolithic applications and easily scale instances without requiring you to manage the VMs. Since 2016, Azure App Services can run single instances of Docker containers as well, simplifying deployment. As a QA environment or a limited production environment, you can deploy multiple Docker host VMs and balance them using the Azure balancer, as shown in Figure 4-3. This lets you manage scaling with a coarse-grain approach, because the whole application lives within a single container.
  • 40. 21 CHAPTER 3 | Architecting container and microservice-based applications Figure 4-3. Example of multiple hosts scaling up a single container application Deployment to the various hosts can be managed with traditional deployment techniques. Docker hosts can be managed with commands like docker run or docker-compose performed manually, or through automation such as continuous delivery (CD) pipelines. Deploying a monolithic application as a container There are benefits to using containers to manage monolithic application deployments. Scaling container instances is far faster and easier than deploying additional VMs. Even if you use virtual machine scale sets, VMs take time to start. When deployed as traditional application instances instead of containers, the configuration of the application is managed as part of the VM, which isn’t ideal. Deploying updates as Docker images is far faster and network efficient. Docker images typically start in seconds, which speeds rollouts. Tearing down a Docker image instance is as easy as issuing a docker stop command, and typically completes in less than a second. Because containers are immutable by design, you never need to worry about corrupted VMs. In contrast, update scripts for a VM might forget to account for some specific configuration or file left on disk. While monolithic applications can benefit from Docker, we’re touching only on the benefits. Additional benefits of managing containers come from deploying with container orchestrators, which manage the various instances and lifecycle of each container instance. Breaking up the monolithic application into subsystems that can be scaled, developed, and deployed individually is your entry point into the realm of microservices. Publishing a single-container-based application to Azure App Service Whether you want to get validation of a container deployed to Azure or when an application is simply a single-container application, Azure App Service provides a great way to provide scalable single- container-based services. Using Azure App Service is simple. It provides great integration with Git to make it easy to take your code, build it in Visual Studio, and deploy it directly to Azure.
  • 41. 22 CHAPTER 3 | Architecting container and microservice-based applications Figure 4-4. Publishing a single-container application to Azure App Service from Visual Studio 2022 Without Docker, if you needed other capabilities, frameworks, or dependencies that aren’t supported in Azure App Service, you had to wait until the Azure team updated those dependencies in App Service. Or you had to switch to other services like Azure Cloud Services or VMs, where you had further control and you could install a required component or framework for your application. Container support in Visual Studio 2017 and later gives you the ability to include whatever you want in your application environment, as shown in Figure 4-4. Since you’re running it in a container, if you add a dependency to your application, you can include the dependency in your Dockerfile or Docker image. As also shown in Figure 4-4, the publish flow pushes an image through a container registry. This can be the Azure Container Registry (a registry close to your deployments in Azure and secured by Azure Active Directory groups and accounts), or any other Docker registry, like Docker Hub or an on- premises registry. Manage state and data in Docker applications In most cases, you can think of a container as an instance of a process. A process doesn’t maintain persistent state. While a container can write to its local storage, assuming that an instance will be around indefinitely would be like assuming that a single location in memory will be durable. You
  • 42. 23 CHAPTER 3 | Architecting container and microservice-based applications should assume that container images, like processes, have multiple instances or will eventually be killed. If they’re managed with a container orchestrator, you should assume that they might get moved from one node or VM to another. The following solutions are used to manage data in Docker applications: From the Docker host, as Docker Volumes: • Volumes are stored in an area of the host filesystem that’s managed by Docker. • Bind mounts can map to any folder in the host filesystem, so access can’t be controlled from Docker process and can pose a security risk as a container could access sensitive OS folders. • tmpfs mounts are like virtual folders that only exist in the host’s memory and are never written to the filesystem. From remote storage: • Azure Storage, which provides geo-distributable storage, providing a good long-term persistence solution for containers. • Remote relational databases like Azure SQL Database or NoSQL databases like Azure Cosmos DB, or cache services like Redis. From the Docker container: • Overlay File System. This Docker feature implements a copy-on-write task that stores updated information to the root file system of the container. That information is “on top” of the original image on which the container is based. If the container is deleted from the system, those changes are lost. Therefore, while it’s possible to save the state of a container within its local storage, designing a system around this would conflict with the premise of container design, which by default is stateless. However, using Docker Volumes is now the preferred way to handle local data in Docker. If you need more information about storage in containers check on Docker storage drivers and About storage drivers. The following provides more detail about these options: Volumes are directories mapped from the host OS to directories in containers. When code in the container has access to the directory, that access is actually to a directory on the host OS. This directory is not tied to the lifetime of the container itself, and the directory is managed by Docker and isolated from the core functionality of the host machine. Thus, data volumes are designed to persist data independently of the life of the container. If you delete a container or an image from the Docker host, the data persisted in the data volume isn’t deleted. Volumes can be named or anonymous (the default). Named volumes are the evolution of Data Volume Containers and make it easy to share data between containers. Volumes also support volume drivers that allow you to store data on remote hosts, among other options. Bind mounts are available since a long time ago and allow the mapping of any folder to a mount point in a container. Bind mounts have more limitations than volumes and some important security issues, so volumes are the recommended option.
  • 43. 24 CHAPTER 3 | Architecting container and microservice-based applications tmpfs mounts are basically virtual folders that live only in the host’s memory and are never written to the filesystem. They are fast and secure but use memory and are only meant for temporary, non- persistent data. As shown in Figure 4-5, regular Docker volumes can be stored outside of the containers themselves but within the physical boundaries of the host server or VM. However, Docker containers can’t access a volume from one host server or VM to another. In other words, with these volumes, it isn’t possible to manage data shared between containers that run on different Docker hosts, although it could be achieved with a volume driver that supports remote hosts. Figure 4-5. Volumes and external data sources for container-based applications Volumes can be shared between containers, but only in the same host, unless you use a remote driver that supports remote hosts. In addition, when Docker containers are managed by an orchestrator, containers might “move” between hosts, depending on the optimizations performed by the cluster. Therefore, it isn’t recommended that you use data volumes for business data. But they’re a good mechanism to work with trace files, temporal files, or similar that will not impact business data consistency. Remote data sources and cache tools like Azure SQL Database, Azure Cosmos DB, or a remote cache like Redis can be used in containerized applications the same way they are used when developing without containers. This is a proven way to store business application data. Azure Storage. Business data usually will need to be placed in external resources or databases, like Azure Storage. Azure Storage, in concrete, provides the following services in the cloud: • Blob storage stores unstructured object data. A blob can be any type of text or binary data, such as document or media files (images, audio, and video files). Blob storage is also referred to as Object storage.
  • 44. 25 CHAPTER 3 | Architecting container and microservice-based applications • File storage offers shared storage for legacy applications using standard SMB protocol. Azure virtual machines and cloud services can share file data across application components via mounted shares. On-premises applications can access file data in a share via the File service REST API. • Table storage stores structured datasets. Table storage is a NoSQL key-attribute data store, which allows rapid development and fast access to large quantities of data. Relational databases and NoSQL databases. There are many choices for external databases, from relational databases like SQL Server, PostgreSQL, Oracle, or NoSQL databases like Azure Cosmos DB, MongoDB, etc. These databases are not going to be explained as part of this guide since they are in a completely different subject. Service-oriented architecture Service-oriented architecture (SOA) was an overused term and has meant different things to different people. But as a common denominator, SOA means that you structure your application by decomposing it into multiple services (most commonly as HTTP services) that can be classified as different types like subsystems or tiers. Those services can now be deployed as Docker containers, which solves deployment issues, because all the dependencies are included in the container image. However, when you need to scale up SOA applications, you might have scalability and availability challenges if you’re deploying based on single Docker hosts. This is where Docker clustering software or an orchestrator can help you, as explained in later sections where deployment approaches for microservices are described. Docker containers are useful (but not required) for both traditional service-oriented architectures and the more advanced microservices architectures. Microservices derive from SOA, but SOA is different from microservices architecture. Features like large central brokers, central orchestrators at the organization level, and the Enterprise Service Bus (ESB) are typical in SOA. But in most cases, these are anti-patterns in the microservice community. In fact, some people argue that “The microservice architecture is SOA done right.” This guide focuses on microservices, because a SOA approach is less prescriptive than the requirements and techniques used in a microservice architecture. If you know how to build a microservice-based application, you also know how to build a simpler service-oriented application. Microservices architecture As the name implies, a microservices architecture is an approach to building a server application as a set of small services. That means a microservices architecture is mainly oriented to the back-end, although the approach is also being used for the front end. Each service runs in its own process and communicates with other processes using protocols such as HTTP/HTTPS, WebSockets, or AMQP. Each microservice implements a specific end-to-end domain or business capability within a certain context boundary, and each must be developed autonomously and be deployable independently. Finally, each microservice should own its related domain data model and domain logic (sovereignty
  • 45. 26 CHAPTER 3 | Architecting container and microservice-based applications and decentralized data management) and could be based on different data storage technologies (SQL, NoSQL) and different programming languages. What size should a microservice be? When developing a microservice, size shouldn’t be the important point. Instead, the important point should be to create loosely coupled services so you have autonomy of development, deployment, and scale, for each service. Of course, when identifying and designing microservices, you should try to make them as small as possible as long as you don’t have too many direct dependencies with other microservices. More important than the size of the microservice is the internal cohesion it must have and its independence from other services. Why a microservices architecture? In short, it provides long-term agility. Microservices enable better maintainability in complex, large, and highly-scalable systems by letting you create applications based on many independently deployable services that each have granular and autonomous lifecycles. As an additional benefit, microservices can scale out independently. Instead of having a single monolithic application that you must scale out as a unit, you can instead scale out specific microservices. That way, you can scale just the functional area that needs more processing power or network bandwidth to support demand, rather than scaling out other areas of the application that don’t need to be scaled. That means cost savings because you need less hardware. Figure 4-6. Monolithic deployment versus the microservices approach As Figure 4-6 shows, in the traditional monolithic approach, the application scales by cloning the whole app in several servers/VM. In the microservices approach, functionality is segregated in smaller services, so each service can scale independently. The microservices approach allows agile changes and rapid iteration of each microservice, because you can change specific, small areas of complex, large, and scalable applications. Architecting fine-grained microservices-based applications enables continuous integration and continuous delivery practices. It also accelerates delivery of new functions into the application. Fine- grained composition of applications also allows you to run and test microservices in isolation, and to
  • 46. 27 CHAPTER 3 | Architecting container and microservice-based applications evolve them autonomously while maintaining clear contracts between them. As long as you don’t change the interfaces or contracts, you can change the internal implementation of any microservice or add new functionality without breaking other microservices. The following are important aspects to enable success in going into production with a microservices- based system: • Monitoring and health checks of the services and infrastructure. • Scalable infrastructure for the services (that is, cloud and orchestrators). • Security design and implementation at multiple levels: authentication, authorization, secrets management, secure communication, etc. • Rapid application delivery, usually with different teams focusing on different microservices. • DevOps and CI/CD practices and infrastructure. Of these, only the first three are covered or introduced in this guide. The last two points, which are related to application lifecycle, are covered in the additional Containerized Docker Application Lifecycle with Microsoft Platform and Tools e-book. Additional resources • Mark Russinovich. Microservices: An application revolution powered by the cloud https://azure.microsoft.com/blog/microservices-an-application-revolution-powered-by-the- cloud/ • Martin Fowler. Microservices https://www.martinfowler.com/articles/microservices.html • Martin Fowler. Microservice Prerequisites https://martinfowler.com/bliki/MicroservicePrerequisites.html • Jimmy Nilsson. Chunk Cloud Computing https://www.infoq.com/articles/CCC-Jimmy-Nilsson • Cesar de la Torre. Containerized Docker Application Lifecycle with Microsoft Platform and Tools (downloadable e-book) https://aka.ms/dockerlifecycleebook Data sovereignty per microservice An important rule for microservices architecture is that each microservice must own its domain data and logic. Just as a full application owns its logic and data, so must each microservice own its logic and data under an autonomous lifecycle, with independent deployment per microservice. This means that the conceptual model of the domain will differ between subsystems or microservices. Consider enterprise applications, where customer relationship management (CRM) applications,
  • 47. 28 CHAPTER 3 | Architecting container and microservice-based applications transactional purchase subsystems, and customer support subsystems each call on unique customer entity attributes and data, and where each employs a different Bounded Context (BC). This principle is similar in Domain-driven design (DDD), where each Bounded Context or autonomous subsystem or service must own its domain model (data plus logic and behavior). Each DDD Bounded Context correlates to one business microservice (one or several services). This point about the Bounded Context pattern is expanded in the next section. On the other hand, the traditional (monolithic data) approach used in many applications is to have a single centralized database or just a few databases. This is often a normalized SQL database that’s used for the whole application and all its internal subsystems, as shown in Figure 4-7. Figure 4-7. Data sovereignty comparison: monolithic database versus microservices In the traditional approach, there’s a single database shared across all services, typically in a tiered architecture. In the microservices approach, each microservice owns its model/data. The centralized database approach initially looks simpler and seems to enable reuse of entities in different subsystems to make everything consistent. But the reality is you end up with huge tables that serve many different subsystems, and that include attributes and columns that aren’t needed in most cases. It’s like trying to use the same physical map for hiking a short trail, taking a day-long car trip, and learning geography. A monolithic application with typically a single relational database has two important benefits: ACID transactions and the SQL language, both working across all the tables and data related to your application. This approach provides a way to easily write a query that combines data from multiple tables. However, data access becomes much more complicated when you move to a microservices architecture. Even when using ACID transactions within a microservice or Bounded Context, it is crucial to consider that the data owned by each microservice is private to that microservice and should only
  • 48. 29 CHAPTER 3 | Architecting container and microservice-based applications be accessed either synchronously through its API endpoints(REST, gRPC, SOAP, etc) or asynchronously via messaging(AMQP or similar). Encapsulating the data ensures that the microservices are loosely coupled and can evolve independently of one another. If multiple services were accessing the same data, schema updates would require coordinated updates to all the services. This would break the microservice lifecycle autonomy. But distributed data structures mean that you can’t make a single ACID transaction across microservices. This in turn means you must use eventual consistency when a business process spans multiple microservices. This is much harder to implement than simple SQL joins, because you can’t create integrity constraints or use distributed transactions between separate databases, as we’ll explain later on. Similarly, many other relational database features aren’t available across multiple microservices. Going even further, different microservices often use different kinds of databases. Modern applications store and process diverse kinds of data, and a relational database isn’t always the best choice. For some use cases, a NoSQL database such as Azure CosmosDB or MongoDB might have a more convenient data model and offer better performance and scalability than a SQL database like SQL Server or Azure SQL Database. In other cases, a relational database is still the best approach. Therefore, microservices-based applications often use a mixture of SQL and NoSQL databases, which is sometimes called the polyglot persistence approach. A partitioned, polyglot-persistent architecture for data storage has many benefits. These include loosely coupled services and better performance, scalability, costs, and manageability. However, it can introduce some distributed data management challenges, as explained in “Identifying domain-model boundaries” later in this chapter. The relationship between microservices and the Bounded Context pattern The concept of microservice derives from the Bounded Context (BC) pattern in domain-driven design (DDD). DDD deals with large models by dividing them into multiple BCs and being explicit about their boundaries. Each BC must have its own model and database; likewise, each microservice owns its related data. In addition, each BC usually has its own ubiquitous language to help communication between software developers and domain experts. Those terms (mainly domain entities) in the ubiquitous language can have different names in different Bounded Contexts, even when different domain entities share the same identity (that is, the unique ID that’s used to read the entity from storage). For instance, in a user-profile Bounded Context, the User domain entity might share identity with the Buyer domain entity in the ordering Bounded Context. A microservice is therefore like a Bounded Context, but it also specifies that it’s a distributed service. It’s built as a separate process for each Bounded Context, and it must use the distributed protocols noted earlier, like HTTP/HTTPS, WebSockets, or AMQP. The Bounded Context pattern, however, doesn’t specify whether the Bounded Context is a distributed service or if it’s simply a logical boundary (such as a generic subsystem) within a monolithic-deployment application. It’s important to highlight that defining a service for each Bounded Context is a good place to start. But you don’t have to constrain your design to it. Sometimes you must design a Bounded Context or
  • 49. 30 CHAPTER 3 | Architecting container and microservice-based applications business microservice composed of several physical services. But ultimately, both patterns -Bounded Context and microservice- are closely related. DDD benefits from microservices by getting real boundaries in the form of distributed microservices. But ideas like not sharing the model between microservices are what you also want in a Bounded Context. Additional resources • Chris Richardson. Pattern: Database per service https://microservices.io/patterns/data/database-per-service.html • Martin Fowler. BoundedContext https://martinfowler.com/bliki/BoundedContext.html • Martin Fowler. PolyglotPersistence https://martinfowler.com/bliki/PolyglotPersistence.html • Alberto Brandolini. Strategic Domain Driven Design with Context Mapping https://www.infoq.com/articles/ddd-contextmapping Logical architecture versus physical architecture It’s useful at this point to stop and discuss the distinction between logical architecture and physical architecture, and how this applies to the design of microservice-based applications. To begin, building microservices doesn’t require the use of any specific technology. For instance, Docker containers aren’t mandatory to create a microservice-based architecture. Those microservices could also be run as plain processes. Microservices is a logical architecture. Moreover, even when a microservice could be physically implemented as a single service, process, or container (for simplicity’s sake, that’s the approach taken in the initial version of eShopOnContainers), this parity between business microservice and physical service or container isn’t necessarily required in all cases when you build a large and complex application composed of many dozens or even hundreds of services. This is where there’s a difference between an application’s logical architecture and physical architecture. The logical architecture and logical boundaries of a system do not necessarily map one- to-one to the physical or deployment architecture. It can happen, but it often doesn’t. Although you might have identified certain business microservices or Bounded Contexts, it doesn’t mean that the best way to implement them is always by creating a single service (such as an ASP.NET Web API) or single Docker container for each business microservice. Having a rule saying each business microservice has to be implemented using a single service or container is too rigid. Therefore, a business microservice or Bounded Context is a logical architecture that might coincide (or not) with physical architecture. The important point is that a business microservice or Bounded Context must be autonomous by allowing code and state to be independently versioned, deployed, and scaled.
  • 50. 31 CHAPTER 3 | Architecting container and microservice-based applications As Figure 4-8 shows, the catalog business microservice could be composed of several services or processes. These could be multiple ASP.NET Web API services or any other kind of services using HTTP or any other protocol. More importantly, the services could share the same data, as long as these services are cohesive with respect to the same business domain. Figure 4-8. Business microservice with several physical services The services in the example share the same data model because the Web API service targets the same data as the Search service. So, in the physical implementation of the business microservice, you’re splitting that functionality so you can scale each of those internal services up or down as needed. Maybe the Web API service usually needs more instances than the Search service, or vice versa. In short, the logical architecture of microservices doesn’t always have to coincide with the physical deployment architecture. In this guide, whenever we mention a microservice, we mean a business or logical microservice that could map to one or more (physical) services. In most cases, this will be a single service, but it might be more. Challenges and solutions for distributed data management Challenge #1: How to define the boundaries of each microservice Defining microservice boundaries is probably the first challenge anyone encounters. Each microservice has to be a piece of your application and each microservice should be autonomous with all the benefits and challenges that it conveys. But how do you identify those boundaries? First, you need to focus on the application’s logical domain models and related data. Try to identify decoupled islands of data and different contexts within the same application. Each context could have a different business language (different business terms). The contexts should be defined and managed independently. The terms and entities that are used in those different contexts might sound similar, but you might discover that in a particular context, a business concept with one is used for a different
  • 51. 32 CHAPTER 3 | Architecting container and microservice-based applications purpose in another context, and might even have a different name. For instance, a user can be referred as a user in the identity or membership context, as a customer in a CRM context, as a buyer in an ordering context, and so forth. The way you identify boundaries between multiple application contexts with a different domain for each context is exactly how you can identify the boundaries for each business microservice and its related domain model and data. You always attempt to minimize the coupling between those microservices. This guide goes into more detail about this identification and domain model design in the section Identifying domain-model boundaries for each microservice later. Challenge #2: How to create queries that retrieve data from several microservices A second challenge is how to implement queries that retrieve data from several microservices, while avoiding chatty communication to the microservices from remote client apps. An example could be a single screen from a mobile app that needs to show user information that’s owned by the basket, catalog, and user identity microservices. Another example would be a complex report involving many tables located in multiple microservices. The right solution depends on the complexity of the queries. But in any case, you’ll need a way to aggregate information if you want to improve the efficiency in the communications of your system. The most popular solutions are the following. API Gateway. For simple data aggregation from multiple microservices that own different databases, the recommended approach is an aggregation microservice referred to as an API Gateway. However, you need to be careful about implementing this pattern, because it can be a choke point in your system, and it can violate the principle of microservice autonomy. To mitigate this possibility, you can have multiple fined-grained API Gateways each one focusing on a vertical “slice” or business area of the system. The API Gateway pattern is explained in more detail in the API Gateway section later. CQRS with query/reads tables. Another solution for aggregating data from multiple microservices is the Materialized View pattern. In this approach, you generate, in advance (prepare denormalized data before the actual queries happen), a read-only table with the data that’s owned by multiple microservices. The table has a format suited to the client app’s needs. Consider something like the screen for a mobile app. If you have a single database, you might pull together the data for that screen using a SQL query that performs a complex join involving multiple tables. However, when you have multiple databases, and each database is owned by a different microservice, you cannot query those databases and create a SQL join. Your complex query becomes a challenge. You can address the requirement using a CQRS approach—you create a denormalized table in a different database that’s used just for queries. The table can be designed specifically for the data you need for the complex query, with a one-to-one relationship between fields needed by your application’s screen and the columns in the query table. It could also serve for reporting purposes. This approach not only solves the original problem (how to query and join across microservices), but it also improves performance considerably when compared with a complex join, because you already have the data that the application needs in the query table. Of course, using Command and Query Responsibility Segregation (CQRS) with query/reads tables means additional development work, and you’ll need to embrace eventual consistency. Nonetheless, requirements on performance and high
  • 52. 33 CHAPTER 3 | Architecting container and microservice-based applications scalability in collaborative scenarios (or competitive scenarios, depending on the point of view) are where you should apply CQRS with multiple databases. “Cold data” in central databases. For complex reports and queries that might not require real-time data, a common approach is to export your “hot data” (transactional data from the microservices) as “cold data” into large databases that are used only for reporting. That central database system can be a Big Data-based system, like Hadoop, a data warehouse like one based on Azure SQL Data Warehouse, or even a single SQL database that’s used just for reports (if size won’t be an issue). Keep in mind that this centralized database would be used only for queries and reports that do not need real-time data. The original updates and transactions, as your source of truth, have to be in your microservices data. The way you would synchronize data would be either by using event-driven communication (covered in the next sections) or by using other database infrastructure import/export tools. If you use event-driven communication, that integration process would be similar to the way you propagate data as described earlier for CQRS query tables. However, if your application design involves constantly aggregating information from multiple microservices for complex queries, it might be a symptom of a bad design -a microservice should be as isolated as possible from other microservices. (This excludes reports/analytics that always should use cold-data central databases.) Having this problem often might be a reason to merge microservices. You need to balance the autonomy of evolution and deployment of each microservice with strong dependencies, cohesion, and data aggregation. Challenge #3: How to achieve consistency across multiple microservices As stated previously, the data owned by each microservice is private to that microservice and can only be accessed using its microservice API. Therefore, a challenge presented is how to implement end-to- end business processes while keeping consistency across multiple microservices. To analyze this problem, let’s look at an example from the eShopOnContainers reference application. The Catalog microservice maintains information about all the products, including the product price. The Basket microservice manages temporal data about product items that users are adding to their shopping baskets, which includes the price of the items at the time they were added to the basket. When a product’s price is updated in the catalog, that price should also be updated in the active baskets that hold that same product, plus the system should probably warn the user saying that a particular item’s price has changed since they added it to their basket. In a hypothetical monolithic version of this application, when the price changes in the products table, the catalog subsystem could simply use an ACID transaction to update the current price in the Basket table. However, in a microservices-based application, the Product and Basket tables are owned by their respective microservices. No microservice should ever include tables/storage owned by another microservice in its own transactions, not even in direct queries, as shown in Figure 4-9.
  • 53. 34 CHAPTER 3 | Architecting container and microservice-based applications Figure 4-9. A microservice can’t directly access a table in another microservice The Catalog microservice shouldn’t update the Basket table directly, because the Basket table is owned by the Basket microservice. To make an update to the Basket microservice, the Catalog microservice should use eventual consistency probably based on asynchronous communication such as integration events (message and event-based communication). This is how the eShopOnContainers reference application performs this type of consistency across microservices. As stated by the CAP theorem, you need to choose between availability and ACID strong consistency. Most microservice-based scenarios demand availability and high scalability as opposed to strong consistency. Mission-critical applications must remain up and running, and developers can work around strong consistency by using techniques for working with weak or eventual consistency. This is the approach taken by most microservice-based architectures. Moreover, ACID-style or two-phase commit transactions are not just against microservices principles; most NoSQL databases (like Azure Cosmos DB, MongoDB, etc.) do not support two-phase commit transactions, typical in distributed databases scenarios. However, maintaining data consistency across services and databases is essential. This challenge is also related to the question of how to propagate changes across multiple microservices when certain data needs to be redundant—for example, when you need to have the product’s name or description in the Catalog microservice and the Basket microservice. A good solution for this problem is to use eventual consistency between microservices articulated through event-driven communication and a publish-and-subscribe system. These topics are covered in the section Asynchronous event-driven communication later in this guide.
  • 54. 35 CHAPTER 3 | Architecting container and microservice-based applications Challenge #4: How to design communication across microservice boundaries Communicating across microservice boundaries is a real challenge. In this context, communication doesn’t refer to what protocol you should use (HTTP and REST, AMQP, messaging, and so on). Instead, it addresses what communication style you should use, and especially how coupled your microservices should be. Depending on the level of coupling, when failure occurs, the impact of that failure on your system will vary significantly. In a distributed system like a microservices-based application, with so many artifacts moving around and with distributed services across many servers or hosts, components will eventually fail. Partial failure and even larger outages will occur, so you need to design your microservices and the communication across them considering the common risks in this type of distributed system. A popular approach is to implement HTTP (REST)-based microservices, due to their simplicity. An HTTP-based approach is perfectly acceptable; the issue here is related to how you use it. If you use HTTP requests and responses just to interact with your microservices from client applications or from API Gateways, that’s fine. But if you create long chains of synchronous HTTP calls across microservices, communicating across their boundaries as if the microservices were objects in a monolithic application, your application will eventually run into problems. For instance, imagine that your client application makes an HTTP API call to an individual microservice like the Ordering microservice. If the Ordering microservice in turn calls additional microservices using HTTP within the same request/response cycle, you’re creating a chain of HTTP calls. It might sound reasonable initially. However, there are important points to consider when going down this path: • Blocking and low performance. Due to the synchronous nature of HTTP, the original request doesn’t get a response until all the internal HTTP calls are finished. Imagine if the number of these calls increases significantly and at the same time one of the intermediate HTTP calls to a microservice is blocked. The result is that performance is impacted, and the overall scalability will be exponentially affected as additional HTTP requests increase. • Coupling microservices with HTTP. Business microservices shouldn’t be coupled with other business microservices. Ideally, they shouldn’t “know” about the existence of other microservices. If your application relies on coupling microservices as in the example, achieving autonomy per microservice will be almost impossible. • Failure in any one microservice. If you implemented a chain of microservices linked by HTTP calls, when any of the microservices fails (and eventually they will fail) the whole chain of microservices will fail. A microservice-based system should be designed to continue to work as well as possible during partial failures. Even if you implement client logic that uses retries with exponential backoff or circuit breaker mechanisms, the more complex the HTTP call chains are, the more complex it is to implement a failure strategy based on HTTP. In fact, if your internal microservices are communicating by creating chains of HTTP requests as described, it could be argued that you have a monolithic application, but one based on HTTP between processes instead of intra-process communication mechanisms.
  • 55. Another Random Document on Scribd Without Any Related Topics
  • 56. symbolise splendour and glory, and in embroidered robes, which suggest the patient use of the slow needle, and the variegated harmony of colour attained at last. There is no marriage between Christ and the soul, unless it is robed in the beauty of righteousness and manifold graces of character. In other places we read that the bride "made herself ready," and also that "to her was granted that she should be arrayed in fine linen, clean and white," in which sayings are set forth the double sources of such a garment of the soul. It is a gift from above. It is "put on" by continual effort, based on faith. The picture of the home-coming of the bride follows. She is attended by her maidens, and with them she passes into the palace amid joys and exultation. The psalm stops at the threshold. It is not for the singer to draw back the curtains and let in the day. "The door was shut." The presence of virgin companions waiting on the bride no more interferes with the application of the psalm to Christ and His Church than the similar representation brings confusion into our Lord's parable of the Ten Virgins. Parables and symbols are elastic, and often duplicate their representations of the same thing; and such is the case here. The closing verses are addressed, not to the bride, but to the king, and can only in a very modified way and partially be supposed to pass beyond the Jewish monarch and refer to the true King. Hopes that he might be blessed with fortunate issue of the marriage were quite in place in an epithalamion, and the delicacy of the light touch with which this closing note is struck is noteworthy, especially in contrast with the tone of many famous secular songs of similar import. But much straining is needed to extract a spiritual sense from the words. Perowne truly says that it is "wiser to acknowledge at once the mixed character" of the psalm, and he quotes a sagacious saying of Calvin's to the effect that it is not necessary that every detail should be carefully fitted to Christ. The psalm had a historical basis; and it has also a prophetic meaning, because the king of Israel was himself a type, and Jesus Christ is the fulfilment of the ideal never realised by its successive occupants. Both views of its nature must be kept in view in its interpretation; and it need cause
  • 57. no surprise if, at some points, the rind of prose fact is, so to speak, thicker than at others, or if certain features absolutely refuse to lend themselves to the spiritual interpretation.
  • 58. PSALM XLVI. 1 God is a refuge and stronghold for us, A help in troubles most readily to be found. 2 Therefore we will not fear, though the earth do change, And the mountains reel into the heart of the sea. 3 Let its waters roar and foam; Let mountains shake at its pride. Selah. [Jehovah of hosts is with us; A high tower for us is Jacob's God.] 4 [There is] a river—its branches make glad the city of God The sanctuary of the tabernacles of the Most High. 5 God is in her midst; she shall not be moved: God shall help her at the morning dawn. 6 Nations roared, kingdoms were moved: He gave forth His voice, the earth melts. 7 Jehovah of hosts is with us; A high tower for us is Jacob's God. Selah. 8 Come, behold the deeds of Jehovah, Who has made desolations in the earth. 9 Quelling wars to the end of the earth: The bow He breaks, and hews the spear in splinters; The chariots He burns in the fire. 10 "Desist, and know that I am God: I will be exalted in the nations, I will be exalted in the earth." 11 Jehovah of hosts is with us; A high tower for us is Jacob's God. Selah. There are two events, one or other of which probably supplies the historical basis of this and the two following psalms. One is Jehoshaphat's deliverance from the combined forces of the
  • 59. bordering nations (2 Chron. xx.). Delitzsch adopts this as the occasion of the psalm. But the other more usually accepted reference to the destruction of Sennacherib's army is more probable. Psalms xlvi. and xlviii. have remarkable parallelisms with Isaiah. The noble contrast of the quiet river which makes glad the city of God with a tossing, earth-shaking sea resembles the prophet's threatening that the effect of refusing the "waters of Shiloah which go softly" would be inundation by the strong and mighty river, the Assyrian power. And the emblem is expanded in the striking language of Isa. xxxiii. 21: "The glorious Lord will be unto us a place of broad rivers and streams; wherein shall go no galley with oars." Encircled by the flashing links of that broad moat, Jerusalem sits secure. Again, the central thought of the refrain in the psalm, "The Lord of hosts is with us," is closely allied to the symbolic name which Isaiah gave as a pledge of deliverance, "Immanuel, God with us." The structure is simple. The three strophes into which the psalm falls set forth substantially the same thought, that God's presence is safety and peace, whatever storms may roar. This general theme is exhibited in the first strophe (vv. 1-3) in reference to natural convulsions; in the second (vv. 4-7) in reference to the rage of hostile kingdoms; and in the third (vv. 8-11) men are summoned to behold a recent example of God's delivering might, which establishes the truth of the preceding utterances and has occasioned the psalm. The grand refrain which closes the second and third strophes should probably be restored at the end of ver. 3. In the first strophe the psalmist paints chaos come again, by the familiar figures of a changed earth, tottering mountains sinking in the raging sea from which they rose at creation, and a wild ocean with thunderous dash appalling the ear and yeasty foam terrifying the eye, sweeping in triumphant insolence over all the fair earth. It is prosaic to insist on an allegorical meaning for the picture. It is rather a vivid sketch of utter confusion, dashed in with three or four bold strokes, an impossible case supposed in order to bring out the unshaken calm of those who have God for ark in such a deluge. He
  • 60. is not only a sure refuge and stronghold, but one easy of access when troubles come. There is little good in a fortress, however impregnable, if it is so difficult to reach that a fugitive might be slain a hundred times before he was safe in it. But this high tower, which no foe can scale, can be climbed at a thought, and a wish lifts us within its mighty walls. The psalmist speaks a deep truth, verified in the spiritual life of all ages, when he celebrates the refuge of the devout soul as "most readily to be found." As the text stands, this strophe is a verse too short, and ver. 3 drags if connected with "will not we fear." The restoration of the refrain removes the anomaly in the length of the strophe, and enables us to detach ver. 3 from the preceding. Its sense is then completed, if we regard it as the protasis of a sentence of which the refrain is the apodosis, or if, with Cheyne and others, we take ver. 3, "Let its waters roar," etc.—what of that? "Jehovah of hosts is with us." If the strophe is thus completed, it conforms to the other two, in each of which may be traced a division into two pairs of verses. These two verse-pairs of the first strophe would then be inverted parallelism,— the former putting security in God first, and surrounding trouble second, the latter dealing with the same two subjects, but in reversed sequence. The second strophe brings a new picture to view with impressive suddenness, which is even more vividly dramatic if the refrain is not supplied. Right against the vision of confusion comes one of peace. The abrupt introduction of "a river" as an isolated noun, which dislocates grammatical structure, is almost an exclamation. "There is a river" enfeebles the swing of the original. We might almost translate, "Lo! a river!" Jerusalem was unique among historical cities in that it had no great river. It had one tiny thread of water, of which perhaps the psalmist is thinking. But whether there is here the same contrast between Siloam's gentle flow and the surging waters of hostile powers as Isaiah sets forth in the passage already referred to (Isa. viii. 6), the meaning of this gladdening stream is the ever- flowing communication of God Himself in His grace. The stream is
  • 61. the fountain in flow. In the former strophe we hear the roar of the troubled waters, and see the firm hills toppling into their depths. Now we behold the gentle flow of the river, gliding through the city, with music in its ripples and sunshine in its flash and refreshment in its waters, parting into many arms and yet one in diversity, and bringing life and gladness wherever it comes. Not with noise nor tumult, but in silent communication, God's grace and peace refresh the soul. Power is loud, but Omnipotence is silent. The roar of all the billows is weak when compared with the quiet sliding onwards of that still stream. It has its divisions. As in old days each man's bit of garden was irrigated by a branch led from the stream, so in endless diversity, corresponding to the infinite greatness of the source and the innumerable variety of men's needs, God's grace comes. "All these worketh that one and the selfsame Spirit, dividing to every man severally." The streams gladden the city of God with the gladness of satisfied thirsts, with the gladness which comes from the contact of the human spirit with Divine completeness. So supplied, the city may laugh at besiegers. It has unfailing supplies within itself, and the enemy may cut off all surface streams, but its "water shall be sure." Substantially the same thought is next stated in plain words: "God is in the midst of her." And therefore two things follow. One is unshaken stability, and another is help at the right time—"at the turn of the morning." "The Lord is in the midst of her"—that is a perennial fact. "The Lord shall help her"—that is the "grace for seasonable help." He, not we, determines when the night shall thin away its blackness into morning twilight. But we may be sure that the presence which is the pledge of stability and calm even in storm and darkness will flash into energy of help at the moment when He wills. The same expression is used to mark the time of His looking from the pillar of cloud and troubling the Egyptians, and there may be an allusion to that standing instance of His help here. "It is not for you to know the times and the seasons"; but this we may know —that the Lord of all times will always help at the right time; He will not come so quickly as to anticipate our consciousness of need, nor
  • 62. delay so long as to let us be irrevocably engulfed in the bog. "Jesus loved Martha, and her sister, and Lazarus. When He heard therefore that he was sick, He abode two days still in the same place where He was." Yet He came in time. With what vigour the short, crashing clauses of ver. 6 describe the wrath and turbulence of the nations, and the instantaneous dissolving of their strength into weakness at a word from those awful lips! The verse may be taken as hypothetical or as historical. In either case we see the sequence of events as by a succession of lightning flashes. The hurry of the style, marked by the omission of connecting particles, reflects the swiftness of incident, like Veni, vidi, vici. The utterance of God's will conquers all. At the sound of that voice stillness and a pause of dread fall on the "roar" (same word as in ver. 3) of the nations, like the hush in the woods when thunder rolls. He speaks, and all meaner sounds cease. "The lion hath roared, who shall not fear?" No material vehicle is needed. To every believer in God there is an incomprehensible action of the Divine Will on material things; and no explanations bridge the gulf recognised in the psalmist's broken utterances, which declare sequence and not mode of operation: "He uttered His voice, the earth melted." Again the triumph of the refrain peals forth, with its musical accompaniment prolonging the impression. In it the psalmist gives voice, for himself and his fellows, to their making their own of the general truths which the psalm has been declaring. The two names of God set forth a twofold ground for confidence. "Jehovah of hosts" is all the more emphatic here since the Second Book of the Psalter is usually Elohistic. It proclaims God's eternal, self-existent Being, and His covenant relation, as well as His absolute authority over the ranked forces of the universe, personal or impersonal, spiritual or material. The Lord of all these legions is with us. When we say "The God of Jacob," we reach back into the past and lay hold of the Helper of the men of old as ours. What He has been, He is; what He did, He is doing still. The river is full to-day, though the van of the army did long ago drink and were satisfied. The bright waters are
  • 63. still as pellucid and abundant as then, and the last of the rear-guard will find them the same. The third strophe summons to contemplate with fixed attention the "desolations" made by some great manifestation of God's delivering power. It is presupposed that these are still visible. Broken bows, splintered spears, half-charred chariots, strew the ground, and Israel can go forth without fear and feast their eyes on these tokens of what God has done for them. The language is naturally applied to the relics of Sennacherib's annihilated force. In any case it points to a recent act of God's, the glad surprise of which palpitates all through the psalm. The field of history is littered with broken, abandoned weapons, once flourished in hands long since turned to dust; and the city and throne of God against which they were lifted remain unharmed. The voice which melted the earth speaks at the close of the psalm; not now with destructive energy, but in warning, through which tones of tenderness can be caught. God desires that foes would cease their vain strife before it proves fatal. "Desist" is here an elliptical expression, of which the full form is "Let your hands drop"; or, as we say, "Ground your weapons," and learn how vain is a contest with Him who is God, and whose fixed purpose is that all nations shall know and exalt Him. The prospect hinted at in the last words, of a world submissive to its King, softens the terrors of His destructive manifestations, reveals their inmost purpose, and opens to foes the possibility of passing, not as conquerors, but as subjects, and therefore fellow-citizens, through the gate into the city.
  • 64. PSALM XLVII. 1 All ye peoples, clap [your] hands; Shout to God with joyful cry. 2 For Jehovah is most High [and] dread, A great King over all the earth. 3 He subdues peoples under us, And nations under our feet, 4 He chooses for us our inheritance, The pride of Jacob whom He loved. Selah. 5 God is gone up with a shout, Jehovah with trumpet clang. 6 Sing with the harp to God, sing with the harp: Sing with the harp to our King, sing with the harp. 7 For King of all the earth is God: Sing with the harp a skilful song. 8 God has become King over the nations: He has taken His seat on His holy throne. 9 The princes of the peoples gather themselves together [As] a people of the God of Abraham: For to God belong the shields of the earth; Greatly has He exalted Himself. The closing thought of Psalm xlvi. is nobly expanded in this jubilant summons to all nations to praise Jehovah as their King. Both psalms have a similar, and probably the same, historical basis: a Divine act so recent that the tumult of triumph has not yet subsided, and the waves of joy still run high. Only in Psalm xlvi. the effect of that God- wrought deliverance is principally regarded as the security and peace of Israel, and in this psalm as the drawing of the nations to obey Israel's King, and so to join the chorus of Israel's praise. While the
  • 65. psalm has many resemblances to the Songs of the King (Psalm xciii. seqq.), it is clearly in its right place here, as forming with the preceding and succeeding psalms a trilogy, occasioned by one great manifestation of God's care for the nation. No event is more appropriate than the usually accepted destruction of Sennacherib's army. The psalm has little of complexity in structure or thought. It is a gush of pure rapture. It rises to prophetic foresight, and, by reason of a comparatively small historical occasion, has a vision of the world-wide expansion of the kingdom of God. It falls into two strophes of four verses each, with one longer verse appended to the latter. In the first strophe the nations are invited to welcome God as their King, not only because of His Divine exaltation and world-wide dominion, but also because of His deeds for "Jacob." The same Divine act which in Psalm xlvi. is represented as quelling wars and melting the earth, and in Psalm xlviii. as bringing dismay, pain, and flight, is here contemplated as attracting the nations to worship. The psalmist knows that destructive providences have their gracious aspect, and that God's true victory over men is not won when opposition is crushed and hearts made to quake, but when recognition of His sway and joy in it swell the heart. The quick clatter of clapping hands in sign of homage to the King (2 Kings xi. 12) blends with the shrill cries with which Easterns express joy, in "a tumult of acclaim." Hupfeld thinks that to suppose the heathen called upon to do homage because of the victory for Israel won over them is entirely mistaken. But unless that victory is the reason for the summons, the psalm offers none; and it is surely not difficult to suppose that the exhibition of God's power leads to reflection which issues in recognition of His sovereignty. Vv. 3, 4, seem to state the grounds for the summons in ver. 1. The tenses in these verses present a difficulty in the way of taking them for a historical retrospect of the conquest and partition of Canaan, which but for that objection would be the natural interpretation. It is possible to take them as "a truth of experience inferred from what had just been witnessed, the historical fact being expressed not in historical
  • 66. form, but generalised and idealised" (Delitzsch, in loc.). The just accomplished deliverance repeated in essence the wonders of the first entrance on possession of the land, and revealed the continuous working of the same Divine hand, ever renewing the choice of Jacob's inheritance, and ever scattering its enemies. "The pride of Jacob" is a phrase in apposition with "our inheritance." The Holy Land was the object of "pride" to "Jacob," not in an evil sense but in that he boasted of it as a precious treasure intrusted to him by God. The root fact of all God's ancient and continued blessings is that He "loved." His own heart, not Jacob's deserts, prompted His mercies. The second strophe is distinguished from the first by the increased fervour of its calls to praise, by its still more exultant rush, and by its omission of reference to Jacob. It is wholly concerned with the peoples whom it invites to take up the song. As in the former strophe the singer showed to the peoples God working in the world, here he bids them look up and see Him ascending on high. "Now that He ascended, what is it but that He also descended first?" The mighty deliverance of which the triumph throbs through this trilogy of pæans of victory was God's coming down. Now He has gone back to His throne and seated Himself thereon, not as having ceased to work in the world—for He is still King over it all—but as having completed a delivering work. He does not withdraw when He goes up. He does not cease to work here below when He sits throned in His palace-temple above. The "shout" and "voice of a trumpet," which accompany that ascent, are borrowed from the ordinary attendants on a triumphal procession. He soars as in a chariot of praises,—from whose lips the psalm does not say, but probably it intends Israel to be understood as the singer. To that choir the nations are called to join their voices and harps, since God is their King too, and not Jacob's only. The word rendered in the A.V. and R.V. (text) "with understanding" is a noun, the name of a description of psalm, which occurs in several psalm titles, and is best understood as "a skilful song." Ver. 8 gathers up the reasons for the peoples' homage to God. He has "become King" over them by His recent act, having manifested and established His dominion; and He
  • 67. has now "sat down on His throne," as having accomplished His purpose, and as thence administering the world's affairs. A final verse, of double the length of the others, stands somewhat apart from the preceding strophe both in rhythm and in thought. It crowns the whole. The invitations to the nations are conceived of as having been welcomed and obeyed. And there rises before the poet's eye a fair picture of a great convocation, such as might wait before a world-ruling monarch's throne on the day of his coronation. The princes of the nations, like tributary kings, come flocking to do homage, "as if they surely knew their sovereign Lord was by." The obliteration of distinction between Israel and the nations, by the incorporation of the latter, so that "the peoples" become part of the "people of the God of Abraham," floats before the singer's prophetic eye, as the end of God's great manifestation of Himself. The two parts of that double choir, which the preceding strophes summon to song, coalesce at last, and in grand unison send up one full- throated, universal melodious shout of praise. "The shields of the earth" are best understood as a figurative expression for the princes just spoken of, who now at last recognise to whom they belong. Thus God has exalted Himself by His deeds; and the result of these deeds is that He is greatly exalted by the praise of a world, in which Israel and the "peoples" dwell as one beneath His sceptre and celebrate His name. The psalmist looked far ahead. His immediate experience was as "a little window through which he saw great matters." The prophecy of the universal spread of God's kingdom and the inclusion in it of the Gentiles is Messianic; and whether the singer knew that he spoke of a fair hope which should not be a fact for weary centuries, or anticipated wider and permanent results from that triumph which inspired his song, he spake of the Christ, and his strains are true prophecies of His dominion. There is no intentional reference in the psalm to the Ascension; but the thoughts underlying its picture of God's going up with a shout are the same which that Ascension sets forth as facts,—the merciful coming down into humanity of the
  • 68. Divine Helper; the completeness of His victory as attested by His return thither where He was before; His session in heaven, not as idle nor wearied, but as having done what He meant to do; His continuous working as King in the world; and the widening recognition of His authority by loving hearts. The psalmist summons us all to swell with our voices that great chorus of praise which, like a sea, rolls and breaks in music round His royal seat.
  • 69. PSALM XLVIII. 1 Great is Jehovah, and much to be praised, In the city of our God, His holy mountain. 2 Lovely in loftiness, a joy of all the earth, Is Mount Zion, the recesses of the north, the city of the great King. 3 God in her palaces Has made Himself known as a high tower. 4 For, lo, the kings assembled themselves, They marched onwards together. 5 They saw, then they were amazed; They were terror-struck, they fled. 6 Trembling seized them there; Pain, as [of] a woman in travail. 7 With an east wind Thou breakest the ships of Tarshish. 8 According as we have heard, so have we seen In the city of Jehovah of hosts, in the city of our God: God will establish her for ever. Selah. 9 We have thought, O God, of Thy loving-kindness In the midst of Thy Temple. 10 According to Thy name, O God, So is Thy praise to the ends of the earth: Thy right hand is full of righteousness. 11 Let Mount Zion rejoice, Let the daughters of Judah exult, Because of Thy judgments. 12 Compass Zion, and walk round her: Reckon her towers.
  • 70. 13 Give heed to her bulwark, Pass through her palaces; That ye may tell it to the generation after. 14 That such is God, our God: For ever and aye He will guide us. Al-Muth. The situation seems the same as in Psalm xlvi., with which this psalm has many points of contact. In both we have the same triumph, the same proud affection for the holy city and sanctuary, the same confidence in God's dwelling there, the same vivid picturing of the mustering of enemies and their rapid dispersion, the same swift movement of style in describing that overthrow, the same thought of the diffusion of God's praise in the world as its consequence, the same closing summons to look upon the tokens of deliverance, with the difference that, in the former psalm, these are the shattered weapons of the defeated foe, and in this the unharmed battlements and palaces of the delivered city. The emphatic word of the refrain in Psalm xlvi. also reappears here in ver. 3. The psalm falls into three parts, of which the first (vv. 1, 2) is introductory, celebrating the glory of Zion as the city of God; the second (vv. 3-8) recounts in glowing words the deliverance of Zion; and the third tells of the consequent praise and trust of the inhabitants of Zion (vv. 9-14). The general sense of the first part is plain, but ver. 2 is difficult. "Mount Zion" is obviously subject, and "lovely in loftiness" and "joy of all the earth" predicates; but the grammatical connection of the two last clauses is obscure. Further, the meaning of "the sides of the north" has not been satisfactorily ascertained. The supposition that there is an allusion in the phrase to the mythological mountain of the gods, with which Zion is compared, is surely most unnatural. Would a Hebrew psalmist be likely to introduce such a parallel, even in order to assert the superiority of Zion? Nor is the grammatical objection to the supposition less serious. It requires a good deal of stretching and inserting to twist the two words "the sides of the
  • 71. north" into a comparison. It is more probable that the clause is topographical, describing some part of the city, but what part is far from clear. The accents make all the verse after "earth" the subject of the two preceding predicates, and place a minor division at "north," implying that "the sides of the north" is more closely connected with "Mount Zion" than with the "city of the great King," or than that last clause is. Following these indications, Stier renders "Mount Zion [and] the northern side (i.e., the lower city, on the north of Zion), which together make the city," etc. Others see here "the Holy City regarded from three points of view"—viz., "the Mount Zion" (the city of David), "the sides of the north" (Mount Moriah and the Temple), "the city of the great King" (Jerusalem proper). So Perowne and others. Delitzsch takes Zion to be the Temple hill, and "the sides of the north" to be in apposition. "The Temple hill or Zion, in the narrower sense, actually formed the north-eastern corner of ancient Jerusalem," says he, and thus regards the subject of the whole sentence as really twofold, not threefold, as appears at first—Zion on the north, which is the palace-temple, and Jerusalem at its feet, which is "the city of the great King." But it must be admitted that no interpretation runs quite smoothly, though the summary ejection of the troublesome words "the sides of the north" from the text is too violent a remedy. But the main thought of this first part is independent of such minute difficulties. It is that the one thing which made Zion-Jerusalem glorious was God's presence in it. It was beautiful in its elevation; it was safely isolated from invaders by precipitous ravines, inclosing the angle of the plateau on which it stood. But it was because God dwelt there and manifested Himself there that it was "a joy for all the earth." The name by which even the earthly Zion is called is "Jehovah-Shammah, The Lord is there." We are not forcing New Testament ideas into Old Testament words when we see in the psalm an eternal truth. An idea is one thing; the fact which more or less perfectly embodies it is another. The idea of God's dwelling with
  • 72. men had its less perfect embodiment in the presence of the Shechinah in the Temple, its more perfect in the dwelling of God in the Church, and will have its complete when the city "having the glory of God" shall appear, and He will dwell with men and be their God. God in her, not anything of her own, makes Zion lovely and gladdening. "Thy beauty was perfect through My comeliness which I had put upon thee, saith the Lord." The second part pictures Zion's deliverance with picturesque vigour (vv. 3-8). Ver. 3 sums up the whole as the act of God, by which He has made Himself known as that which the refrain of Psalm xlvi. declared Him to be—a refuge, or, literally, a high tower. Then follows the muster of the hosts. "The kings were assembled." That phrase need not be called exaggeration, nor throw doubt on the reference to Sennacherib's army, if we remember the policy of Eastern conquerors in raising their armies from their conquests, and the boast which Isaiah puts into the mouth of the Assyrian: "Are not my princes altogether kings?" They advance against the city. "They saw,"—no need to say what. Immediately they "were amazed." The sight of the city broke on them from some hill-crest on their march. Basilisk-like, its beauty was paralysing, and shot a nameless awe into their hearts. "They were terror-struck; they fled." As in Psalm xlvi. 6, the clauses, piled up without cement of connecting particles, convey an impression of hurry, culminating in the rush of panic-struck fugitives. As has been often noticed, they recall Cæsar's Veni, vidi, vici; but these kings came, saw, were conquered. No cause for the rout is named. No weapons were drawn in the city. An unseen hand "smites once, and smites no more"; for once is enough. The process of deliverance is not told; for a hymn of victory is not a chronicle. One image explains it all, and signalises the Divine breath as the sole agent. "Thou breakest the ships of Tarshish with an east wind" is not history, but metaphor. The unwieldy, huge vessel, however strong for fight, is unfit for storms, and, caught in a gale, rolls heavily in the trough of the sea, and is driven on a lee shore and ground to pieces on its rocks. "God blew upon them, and they were scattered," as the medal struck on the defeat of the Armada had it.
  • 73. In the companion psalm God's uttered voice did all. Here the breath of the tempest, which is the breath of His lips, is the sole agent. The past, of which the nation had heard from its fathers, lives again in their own history; and that verification of traditional belief by experience is to a devout soul the chief blessing of its deliverances. There is rapture in the thought that "As we have heard, so have we seen." The present ever seems commonplace. The sky is farthest from earth right overhead, but touches the ground on the horizon behind and before. Miracles were in the past; God will be manifestly in the far-off future, but the present is apt to seem empty of Him. But if we rightly mark His dealings with us, we shall learn that nothing in His past has so passed that it is not present. As the companion psalm says, "The God of Jacob is our refuge," this exclaims, "As we have heard, so have we seen." But not only does the deliverance link the present with the past, but it flings a steady light into the future. "God shall establish her for ever." The city is truly "the eternal city," because God dwells in it. The psalmist was thinking of the duration of the actual Jerusalem, the imperfect embodiment of a great idea. But whatever may be its fate, the heart of his confidence is no false vision; for God's city will outlast the world. Like the "maiden fortresses," of which there is one in almost every land, fondly believed never to have been taken by enemies, that city is inexpugnable, and the confident answer to every threatening assailant is, "The virgin, the daughter of Zion, hath despised thee, and laughed thee to scorn; the daughter of Jerusalem hath shaken her head at thee." "God will establish her for ever." The pledges of that stability are the deliverances of the past and present. The third part (vv. 9-14) deals with the praise and trust of the inhabitants of Zion. Deliverance leads to thankful meditation on the loving-kindness which it so signally displayed, and the ransomed people first gather in the Temple, which was the scene of God's manifestation of His grace, and therefore is the fitting place for them to ponder it. The world-wide consequences of the great act of
  • 74. loving-kindness almost shut out of sight for the moment its bearing on the worshippers. It is a lofty height to which the song climbs, when it regards national deliverance chiefly as an occasion for wider diffusion of God's praise. His "name" is the manifestation of His character in act. The psalmist is sure that wherever that character is declared praise will follow, because he is sure that that character is perfectly and purely good, and that God cannot act but in such a way as to magnify Himself. That great sea will cast up nothing but pearls. The words carry also a lesson for recipients of Divine loving- kindness, teaching them that they misapprehend the purpose of their blessings, if they confine these to their own well-being and lose sight of the higher object—that men may learn to know and love Him. But the deliverance not only produces grateful meditation and widespread praise; it sets the mother city and her daughter villages astir, like Miriam and her maidens, with timbrel and dance, and ringing songs which celebrate "Thy judgments," terrible as they were. That dead host was an awful sight, and hymns of praise seem heartless for its dirge. But it is not savage glee nor fierce hatred which underlies the psalmist's summons, and still less is it selfish joy. "Thy judgments" are to be hymned when they smite some giant evil; and when systems and their upholders that array themselves against God are drowned in some Red Sea, it is fitting that on its banks should echo, "Sing ye to Jehovah, for He hath triumphed gloriously." The close of this part may be slightly separated from vv. 9-11. The citizens who have been cooped up by the siege are bidden to come forth, and, free from fear, to compass the city without, and pass between its palaces within, and so see how untouched they are. The towers and bulwark or rampart remain unharmed, with not a stone smitten from its place. Within, the palaces stand without a trace of damage to their beauty. Whatever perishes in any assaults, that which is of God will abide; and, after all musterings of the enemy, the uncaptured walls will rise in undiminished strength, and the fair palaces which they guard glitter in untarnished splendour. And this complete exemption from harm is to be told to the generation
  • 75. following, that they may learn what a God this God is, and how safely and well He will guide all generations. The last word in the Hebrew text, which the A.V. and R.V. render "even unto death," can scarcely have that meaning. Many attempts have been made to find a signification appropriate to the close of such a triumphal hymn as this, but the simplest and most probable course is to regard the words as a musical note, which is either attached abnormally to the close of the psalm, or has strayed hither from the superscription of Psalm xlix. It is found in the superscription of Psalm ix. ("Al-Muth") as a musical direction, and has in all likelihood the same meaning here. If it is removed, the psalm ends abruptly, but a slight transposition of words and change of the main division of the verse remove that difficulty by bringing "for ever and aye" from the first half. The change improves both halves, laying the stress of the first exclusively on the thought that this God is such a God (or, by another rendering, "is here," i.e., in the city), without bringing in reference to the eternity of His protection, and completing the second half worthily, with the thought of His eternal guidance of the people among whom He dwells.
  • 76. PSALM XLIX. 1 Hear this, all ye peoples; Give ear, all ye inhabitants of the world: 2 Both low-born and high-born, Rich and poor together. 3 My mouth shall speak wisdom; And the meditation of my heart shall utter understanding 4 I will bend my ear to a parable: I will open my riddle on the harp. 5 Why should I fear in the days of evil, When the malice of my pursuers surrounds me, 6 [Even of] those who rely on their riches, And boast of their wealth? 7 No man can at all redeem a brother; He cannot give to God a ransom for him 8 (Yea, too costly is the redemption price of their soul, And he must leave it alone for ever): 9 That he may continue living on for ever, And may not see the pit. 10 Nay, he must see that the wise die The fool and the brutish perish alike, And leave to others their riches. 11 Their inward thought [is that] their houses [shall last] for ever, Their dwellings to generation after generation; They call their lands by their own names. 12 But man [being] in honour abides not: He becomes like the beasts [that] are brought to silence. 13 This is the lot of them to whom presumptuous confidence belongs:
  • 77. And after them men approve their sayings. Selah. 14 Like sheep they are folded in Sheol; Death shepherds them: And the upright shall rule over them in the morning; And their form shall be wasted away by Sheol, So that it is without a dwelling. 15 Surely God shall redeem my soul from the power of Sheol: For He shall take me. Selah. 16 Fear not thou when a man becomes rich, When the glory of his house increases: 17 For when he dies he will not take away any [of it]; His glory shall not go down after him. 18 Though in his lifetime he bless his soul (And [men] praise thee when thou doest well for thyself) 19 He shall go to the generation of his fathers; For evermore they see not light. 20 Man [who is] in honour, and has not understanding, Becomes like the beasts that are brought to silence. This psalm touches the high-water mark of Old Testament faith in a future life; and in that respect, as well as in its application of that faith to alleviate the mystery of present inequalities and non- correspondence of desert with condition, is closely related to the noble Psalm lxxiii., with which it has also several verbal identities. Both have the same problem before them—to construct a theodicy, or "to vindicate the ways of God to man"—and both solve it in the same fashion. Both appear to refer to the story of Enoch in their remarkable expression for ultimate reception into the Divine presence. But whether the psalms are contemporaneous cannot be determined from these data. Cheyne regards the treatment of the theme in Psalm lxxiii. as "more skilful," and therefore presumably later than Psalm xlix., which he would place "somewhat before the close of the Persian period." This date rests on the assumption that the amount of certitude as to a future life expressed in the psalm was not realised in Israel till after the exile.
  • 78. After a solemn summons to all the world to hear the psalmist's utterance of what he has learned by Divine teaching (vv. 1-4), the psalm is divided into two parts, each closed with a refrain. The former of these (vv. 5-12) contrasts the arrogant security of the prosperous godless with the end that awaits them; while the second (vv. 13-20) contrasts the dreary lot of these victims of vain self- confidence with the blessed reception after death into God's own presence which the psalmist grasped as a certainty for himself, and thereon bases an exhortation to possess souls in patience while the godless prosper, and to be sure that their lofty structures will topple into hideous ruin. The psalmist's consciousness that he speaks by Divine inspiration, and that his message imports all men, is grandly expressed in his introductory summons. The very name which he gives to the world suggests the latter thought; for it means—the world considered as fleeting. Since we dwell in so transitory an abode, it becomes us to listen to the deep truths of the psalm. These have a message for high and low, for rich and poor. They are like a keen lancet to let out too great fulness of blood from the former, and to teach moderation, lowliness, and care for the Unseen. They are a calming draught for the latter, soothing when perplexed or harmed by "the proud man's contumely." But the psalmist calls for universal attention, not only because his lessons fit all classes, but because they are in themselves "wisdom," and because he himself had first bent his ear to receive them before he strung his lyre to utter them. The brother- psalmist, in Psalm lxxiii., presents himself as struggling with doubt and painfully groping his way to his conclusion. This psalmist presents himself as a divinely inspired teacher, who has received into purged and attentive ears; in many a whisper from God, and as the result of many an hour of silent waiting, the word which he would now proclaim on the housetops. The discipline of the teacher of religious truth is the same at all times. There must be the bent ear before there is the message which men will recognise as important and true.
  • 79. There is no parable in the ordinary sense in the psalm. The word seems to have acquired the wider meaning of a weighty didactic utterance, as in Psalm lxxviii. 2. The expression "Open my riddle" is ambiguous, and is by some understood to mean the proposal and by others the solution of the puzzle; but the phrase is more naturally understood of solving than of setting a riddle, and if so, the disproportion between the characters and fortunes of good and bad is the mystery or riddle, and the psalm is its solution. The main theme of the first part is the certainty of death, which makes infinitely ludicrous the rich man's arrogance. It is one version of "There is no armour against Fate; Death lays his icy hand on kings." Therefore how vain the boasting in wealth, when all its heaps cannot buy a day of life! This familiar thought is not all the psalmist's contribution to the solution of the mystery of life's unequal partition of worldly good; but it prepares the way for it, and it lays a foundation for his refusal to be afraid, however pressed by insolent enemies. Very significantly he sets the conclusion, to which observation of the transiency of human prosperity has led him, at the beginning of his "parable." In the parallel psalm (lxxiii.) the singer shows himself struggling from the depths of perplexity up to the sunny heights of faith. But here the poet begins with the clear utterance of trustful courage, and then vindicates it by the thought of the impotence of wealth to avert death. The hostility to himself of the self-confident rich boasters appears only for a moment at first. It is described by a gnarled, energetic phrase which has been diversely understood. But it seems clear that the "iniquity" (A.V. and R.V.) spoken of in ver. 5 b is not the psalmist's sin, for a reference here to his guilt or to retribution would be quite irrelevant; and if it were the consequences of his own evil that dogged him at his heels, he had every reason to fear, and confidence would be insolent defiance. But the word rendered in the
  • 80. A.V. heels, which is retained in the R.V. with a change in construction, may be a participial noun, derived from a verb meaning to trip up or supplant; and this gives a natural coherence to the whole verse, and connects it with the following one. "Pursuers" is a weak equivalent for the literal "those who would supplant me," but conveys the meaning, though in a somewhat enfeebled condition. Ver. 6 is a continuance of the description of the supplanters. They are "men of this world," the same type of man as excites stern disapproval in many psalms: as, for instance, in xvii. 14 —a psalm which is closely related to this, both in its portrait of the godless and its lofty hope for the future. It is to be noted that they are not described as vicious or God-denying or defying. They are simply absorbed in the material, and believe that land and money are the real, solid goods. They are the same men as Jesus meant when He said that it was hard for those who trusted in riches to enter into the kingdom of heaven. It has been thought that the existence of such a class points to a late date for the psalm; but the reliance on riches does not require large riches to rely on, and may flourish in full perniciousness in very primitive social conditions. A small elevation suffices to lift a man high enough above his fellows to make a weak head giddy. Those to whom material possessions are the only good have a natural enmity towards those who find their wealth in truth and goodness. The poet, the thinker, and, most of all, the religious man, are targets for more or less active "malice," or, at all events, are recognised as belonging to another class, and regarded as singular and "unpractical," if nothing worse. But the psalmist looks far enough ahead to see the end of all the boasting, and points to the great instance of the impotence of material good— its powerlessness to prolong life. It would be more natural to find in ver. 7 the statement that the rich man cannot prolong his own days than that he cannot do so for a "brother." A very slight change in the text would make the initial word of the verse ("brother") the particle of asseveration, which occurs in ver. 15 (the direct antithesis of this verse), and is characteristic of the parallel Psalm lxxiii. With that reading (Ewald, Cheyne, Baethgen, etc.) other slight difficulties are smoothed; but the present text is attested by the LXX. and other
  • 81. Welcome to our website – the perfect destination for book lovers and knowledge seekers. We believe that every book holds a new world, offering opportunities for learning, discovery, and personal growth. That’s why we are dedicated to bringing you a diverse collection of books, ranging from classic literature and specialized publications to self-development guides and children's books. More than just a book-buying platform, we strive to be a bridge connecting you with timeless cultural and intellectual values. With an elegant, user-friendly interface and a smart search system, you can quickly find the books that best suit your interests. Additionally, our special promotions and home delivery services help you save time and fully enjoy the joy of reading. Join us on a journey of knowledge exploration, passion nurturing, and personal growth every day! ebookbell.com