永続インフラストラクチャとして NoSQL データベースを使用する
ヒント
このコンテンツは eBook の「コンテナー化された .NET アプリケーションの .NET マイクロサービス アーキテクチャ」からの抜粋です。.NET Docs で閲覧できるほか、PDF として無料ダウンロードすると、オンラインで閲覧できます。
インフラストラクチャ データ層に NoSQL データベースを使用する場合、通常は Entity Framework Core のような ORM は使用しません。 代わりに、Azure Cosmos DB、MongoDB、Cassandra、RavenDB、CouchDB、または Azure Storage Table などの NoSQL エンジンによって提供される API を使用します。
ただし、NoSQL データベース、特にドキュメント指向データベース (Azure Cosmos DB、CouchDB、RavenDB など) を使用する場合、集約ルート、子エンティティ クラス、および値オブジェクト クラスに関しては、DDD 集約を使用してモデルを設計する方法は、EF Core での方法と部分的に似ています。 しかし、最終的には、データベースの選択が設計に影響を与えます。
ドキュメント指向データベースを使用する場合は、JSON または別の形式でシリアル化された 1 つのドキュメントとして集約を実装します。 ただし、データベースの使用は、ドメイン モデルのコードの観点からは透過的です。 NoSQL データベースを使用する場合も、エンティティ クラスと集約ルート クラスを引き続き使用しますが、永続化がリレーショナルではないため、EF Core を使用するときよりも高い柔軟性が得られます。
違いは、そのモデルを永続化する方法にあります。 インフラストラクチャの永続性に依存しない、POCO エンティティ クラスに基づいてドメイン モデルを実装した場合、リレーショナルから NoSQL に移行しても、別の永続化インフラストラクチャに移行したように見える場合があります。 しかし、これを目標とはしないでください。 各種のデータベース技術には常に制約や妥協があり、リレーショナルまたは NoSQL データベースに同じモデルを持つことはできません。 永続化モデルの変更は、トランザクションと永続化の操作がかなり異なるため、簡単な作業ではありません。
たとえば、ドキュメント指向のデータベースでは、複数の子コレクション プロパティを持つためにルートを集約することができます。 リレーショナル データベースでは、複数の子コレクション プロパティへのクエリ実行は簡単に最適化されません。UNION ALL SQL ステートメントが EF から返されるためです。 リレーショナル データベースと NoSQL データベースに対して同じドメイン モデルを持つのは単純なことではないので、行わないでください。 データがそれぞれ特定のデータベースでどのように使用されるかを理解したうえで、モデルを設計する必要があります。
NoSQL データベースを使用する場合の利点は、エンティティがより非正規化されるため、テーブル マッピングを設定しなくてもよい点です。 リレーショナル データベースを使用するときよりも、ドメイン モデルに柔軟性を持たせることができます。
集約に基づいてドメイン モデルを設計すると、NoSQL とドキュメント指向データベースへの移行が、リレーショナル データベースを使用するよりもさらに簡単になる場合があります。これは、設計する集約が、ドキュメント指向データベース内のシリアル化されたドキュメントと似ているからです。 その後、これらの "バッグ" にその集約に必要なすべての情報を含めることができます。
たとえば、次の JSON コードは、ドキュメント指向データベースを使用するときの、注文集約のサンプル実装です。 これは、eShopOnContainers サンプルで実装した注文集約と似ていますが、下に EF Core を使用していません。
{
"id": "2024001",
"orderDate": "2/25/2024",
"buyerId": "1234567",
"address": [
{
"street": "100 One Microsoft Way",
"city": "Redmond",
"state": "WA",
"zip": "98052",
"country": "U.S."
}
],
"orderItems": [
{"id": 20240011, "productId": "123456", "productName": ".NET T-Shirt",
"unitPrice": 25, "units": 2, "discount": 0},
{"id": 20240012, "productId": "123457", "productName": ".NET Mug",
"unitPrice": 15, "units": 1, "discount": 0}
]
}
Azure Cosmos DB とネイティブ Cosmos DB API の概要
Azure Cosmos DB は、ミッション クリティカルなアプリケーションのために、世界各地に分散された Microsoft のデータベース サービスです。 Azure Cosmos DB では、ターン キー グローバル配布、世界規模でのスループットとストレージのエラスティック スケーリング、99 パーセンタイルで 10 ミリ秒未満の待機時間、5 つの明確に定義された整合性レベル、および保証された高可用性を提供しており、これらすべてが業界トップの SLA でサポートされています。 Azure Cosmos DB により、データが自動的にインデックス付けされるため、スキーマとインデックスの管理に対処する必要がなくなります。 Azure Cosmos DB はマルチモデルであり、ドキュメント、キーと値、グラフ、列指向の各データ モデルをサポートします。
図 7-19 Azure Cosmos DB のグローバル配布
C# モデルを使用して Azure Cosmos DB API で使用される集約を実装すると、集約が EF Core で使用される C# POCO クラスと同じようになる場合があります。 違いは、次のコードのように、それらをアプリケーション層とインフラストラクチャ層から使用する方法にあります。
// C# EXAMPLE OF AN ORDER AGGREGATE BEING PERSISTED WITH AZURE COSMOS DB API
// *** Domain Model Code ***
// Aggregate: Create an Order object with its child entities and/or value objects.
// Then, use AggregateRoot's methods to add the nested objects so invariants and
// logic is consistent across the nested properties (value objects and entities).
Order orderAggregate = new Order
{
Id = "2024001",
OrderDate = new DateTime(2005, 7, 1),
BuyerId = "1234567",
PurchaseOrderNumber = "PO18009186470"
}
Address address = new Address
{
Street = "100 One Microsoft Way",
City = "Redmond",
State = "WA",
Zip = "98052",
Country = "U.S."
}
orderAggregate.UpdateAddress(address);
OrderItem orderItem1 = new OrderItem
{
Id = 20240011,
ProductId = "123456",
ProductName = ".NET T-Shirt",
UnitPrice = 25,
Units = 2,
Discount = 0;
};
//Using methods with domain logic within the entity. No anemic-domain model
orderAggregate.AddOrderItem(orderItem1);
// *** End of Domain Model Code ***
// *** Infrastructure Code using Cosmos DB Client API ***
Uri collectionUri = UriFactory.CreateDocumentCollectionUri(databaseName,
collectionName);
await client.CreateDocumentAsync(collectionUri, orderAggregate);
// As your app evolves, let's say your object has a new schema. You can insert
// OrderV2 objects without any changes to the database tier.
Order2 newOrder = GetOrderV2Sample("IdForSalesOrder2");
await client.CreateDocumentAsync(collectionUri, newOrder);
ドメイン モデルを操作する方法が、インフラストラクチャが EF のときにドメイン モデル レイヤーで使用するのと同様の方法になる可能性があることがわかります。 集約内の整合性、インバリアント、および検証を確認するため、同じ集約ルート メソッドを引き続き使用します。
ただし、モデルを NoSQL データベースに永続化する場合は、EF Core コードやリレーショナル データベースに関連するその他のコードと比べて、コードと API は劇的に変わります。
MongoDB と Azure Cosmos DB をターゲットとする .NET コードを実装する
.NET コンテナーから Azure Cosmos DB を使用する
他の .NET アプリケーションからアクセスする場合と同じように、コンテナーで実行されている .NET コードから Azure Cosmos DB データベースにアクセスできます。 たとえば、eShopOnContainers の Locations.API と Marketing.API のマイクロサービスは、Azure Cosmos DB データベースを使用できるように実装されています。
ただし、Docker 開発環境の観点から、Azure Cosmos DB には制限があります。 ローカルの開発用コンピューターで実行できるオンプレミスの Azure Cosmos DB エミュレーターがある場合でも、Windows のみがサポートされます。 Linux と macOS はサポートされません。
このエミュレーターを Docker で実行する可能性もありますが、可能性があるのは Windows コンテナーのみであり、Linux コンテナーではありません。 現在、Docker for Windows に Linux コンテナーと Windows コンテナーを同時に展開することはできないため、アプリケーションを Linux コンテナーとして展開する場合、これが開発環境にとって最初のハンディキャップとなります。 展開されるすべてのコンテナーは、Linux または Windows 用のどちらかにする必要があります。
開発およびテスト ソリューションにとって理想的でより簡単な展開は、開発およびテスト環境に常に整合性を持たせるために、データベース システムをカスタム コンテナーと共にコンテナーとして展開できることです。
Linux/Windows コンテナーと Azure Cosmos DB のローカル開発/テストに MongoDB API を使用する
Cosmos DB データベースでは、.NET とネイティブ MongoDB ワイヤ プロトコルのために MongoDB API をサポートしています。 つまり、既存のドライバーを使用することで、図 7-20 に示すように、MongoDB 用に記述されたアプリケーションで Cosmos DB と通信し、MongoDB データベースの代わりに Cosmos DB データベースを使用できるようになります。
図 7-20 MongoDB API とプロトコルを使用して Azure Cosmos DB にアクセスする
MongoDB Docker イメージは、Docker Linux コンテナーと Docker Windows コンテナーをサポートするマルチアーキテクチャ イメージなので、これは Linux コンテナーを使用する Docker 環境での概念実証にとって非常に便利な方法です。
次の画像に示すように、MongoDB API を使用することで、eShopOnContainers でローカル開発環境に対して MongoDB Linux コンテナーと Windows コンテナーの両方がサポートされますが、その後、MongoDB の接続文字列を Azure Cosmos DB をポイントするように変更するだけで、Azure Cosmos DB と同じようなスケーラブルな PaaS クラウド ソリューションに移行できます。
図 7-21 開発環境に MongoDB コンテナーまたは運用に Azure Cosmos DB を使用する eShopOnContainers
運用環境の Azure Cosmos DB は、PaaS およびスケーラブルなサービスとして Azure のクラウドで実行されます。
.NET のカスタム コンテナーは、(Windows 10 コンピューターで Docker for Windows を使用して) ローカルの Docker の開発用ホストで実行することも、Azure AKS または Azure Service Fabric での Kubernetes のように、運用環境に展開することもできます。 この 2 つ目の環境では、運用中のデータの処理にクラウド内の Azure Cosmos DB を使用しているため、.NET のカスタム コンテナーのみが展開され、MongoDB コンテナーは展開されません。
MongoDB API を使用することの明らかな利点は、MongoDB と Azure Cosmos DB の両方のデータベース エンジンでソリューションを実行できるため、異なる環境への移行が容易になることです。 ただし、特定のデータベース エンジンの機能をフル活用するために、ネイティブ API (ネイティブ Cosmos DB API) を使用することには意義がある場合があります。
単純に MongoDB を使用することとクラウドでの Cosmos DB との詳しい比較については、Azure Cosmos DB を使用する利点に関するページを参照してください。
実稼働アプリケーションのアプローチを分析する:MongoDB API とCosmos DB API
Microsoft では、根本的に、Azure Cosmos DB でも動作する NoSQL データベースを使用して一貫性のある開発/テスト環境を持つことを優先したため、eShopOnContainers では MongoDB API を使用しています。
ただし、MongoDB API を使用して実稼働アプリケーションの Azure で Azure Cosmos DB にアクセスすることを計画している場合は、Azure Cosmos DB データベースにアクセスするために MongoDB API を使用した場合とネイティブ Azure Cosmos DB API を使用した場合とを比較して、機能とパフォーマンスの違いを分析する必要があります。 同様の場合は、MongoDB API を使用でき、同時に 2 つの NoSQL データベース エンジンをサポートするという利点が得られます。
また、MongoDB Azure Service を使用して、MongoDB クラスターを Azure のクラウドで運用データベースとして使用することもできます。 ただしこれは、Microsoft が提供している PaaS サービスではありません。 このケースでは、Azure は MongoDB からのそのソリューションをホスティングしているだけです。
基本的に、これは、Linux コンテナーには便利な選択だったため、eShopOnContainers では MongoDB API を使用しましたが、Azure Cosmos DB に対して MongoDB API を常に使用すべきではないことを言明する免責事項にすぎません。 決定は、特定のニーズと、実稼働アプリケーションに対して行う必要があるテストに基づいて行う必要があります。
コード:.NET アプリケーションで MongoDB API を使用する
.NET 用の MongoDB API は、次の図に示されている Locations.API プロジェクトのような、プロジェクトに追加する必要のある NuGet パッケージに基づいています。
図 7-22。 .NET プロジェクト内の MongoDB API NuGet パッケージの参照
次のセクションでは、コードを調査しましょう。
MongoDB API によって使用されるモデル
最初に、データベースから取得したデータをアプリケーションのメモリ領域で保持するモデルを定義する必要があります。 eShopOnContainers で Locations に使用するモデルの例を次に示します。
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver.GeoJsonObjectModel;
using System.Collections.Generic;
public class Locations
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public int LocationId { get; set; }
public string Code { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
public string Parent_Id { get; set; }
public string Description { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public GeoJsonPoint<GeoJson2DGeographicCoordinates> Location
{ get; private set; }
public GeoJsonPolygon<GeoJson2DGeographicCoordinates> Polygon
{ get; private set; }
public void SetLocation(double lon, double lat) => SetPosition(lon, lat);
public void SetArea(List<GeoJson2DGeographicCoordinates> coordinatesList)
=> SetPolygon(coordinatesList);
private void SetPosition(double lon, double lat)
{
Latitude = lat;
Longitude = lon;
Location = new GeoJsonPoint<GeoJson2DGeographicCoordinates>(
new GeoJson2DGeographicCoordinates(lon, lat));
}
private void SetPolygon(List<GeoJson2DGeographicCoordinates> coordinatesList)
{
Polygon = new GeoJsonPolygon<GeoJson2DGeographicCoordinates>(
new GeoJsonPolygonCoordinates<GeoJson2DGeographicCoordinates>(
new GeoJsonLinearRingCoordinates<GeoJson2DGeographicCoordinates>(
coordinatesList)));
}
}
MongoDB NuGet パッケージから取得されるいくつかの属性と型があることがわかります。
NoSQL データベースは、通常、非リレーショナルの階層データを操作するのに非常に適しています。 この例では、GeoJson2DGeographicCoordinates
のように、特に地理上の位置のために作成された MongoDB 型を使用しています。
データベースとコレクションを取得する
eShopOnContainers では、次のコードのように、コードを実装してデータベースと MongoCollections を取得する、カスタムのデータベース コンテキストを作成しました。
public class LocationsContext
{
private readonly IMongoDatabase _database = null;
public LocationsContext(IOptions<LocationSettings> settings)
{
var client = new MongoClient(settings.Value.ConnectionString);
if (client != null)
_database = client.GetDatabase(settings.Value.Database);
}
public IMongoCollection<Locations> Locations
{
get
{
return _database.GetCollection<Locations>("Locations");
}
}
}
データを取得する
C# コードでは、Web API コントローラーやカスタム リポジトリの実装のように、MongoDB API からクエリを実行するときに、次のようなコードを記述できます。 _context
オブジェクトは以前の LocationsContext
クラスのインスタンスであることに注意してください。
public async Task<Locations> GetAsync(int locationId)
{
var filter = Builders<Locations>.Filter.Eq("LocationId", locationId);
return await _context.Locations
.Find(filter)
.FirstOrDefaultAsync();
}
MongoDB の接続文字列に対して docker-compose.override.yml ファイル内の環境変数を使用する
MongoClient オブジェクトを作成するときには、正確に適切なデータベースをポイントしている ConnectionString
パラメーターと同じ、基本的なパラメーターが必要です。 eShopOnContainers の場合、接続文字列はローカル MongoDB Docker コンテナーまたは "運用中の" Azure Cosmos DB データベースをポイントすることができます。 次の yml コードにあるように、その接続文字列は、docker-compose または Visual Studio を使用して展開するときに使用される、docker-compose.override.yml
ファイルで定義されている環境変数から取得されます。
# docker-compose.override.yml
version: '3.4'
services:
# Other services
locations-api:
environment:
# Other settings
- ConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosqldata}
重要
Microsoft では、使用可能な最も安全な認証フローを使用することをお勧めします。 Azure SQL に接続する場合は、Azure リソースの管理 ID が推奨される認証方法です。
ConnectionString
環境変数はこの方法で解決されます:ESHOP_AZURE_COSMOSDB
グローバル変数が Azure Cosmos DB 接続文字列を使用して .env
ファイルで定義されている場合、そのグローバル変数を使用してクラウド内の Azure Cosmos DB データベースにアクセスします。 定義されていない場合は mongodb://nosqldata
値が取得され、開発 MongoDB コンテナーが使用されます。
次のコードは、eShopOnContainers に実装されているように、Azure Cosmos DB グローバル環境変数を持つ .env
ファイルを示しています。
# .env file, in eShopOnContainers root folder
# Other Docker environment variables
ESHOP_EXTERNAL_DNS_NAME_OR_IP=host.docker.internal
ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=<YourDockerHostIP>
#ESHOP_AZURE_COSMOSDB=<YourAzureCosmosDBConnData>
#Other environment variables for additional Azure infrastructure assets
#ESHOP_AZURE_REDIS_BASKET_DB=<YourAzureRedisBasketInfo>
#ESHOP_AZURE_STORAGE_CATALOG_URL=<YourAzureStorage_Catalog_BLOB_URL>
#ESHOP_AZURE_SERVICE_BUS=<YourAzureServiceBusInfo>
「Azure Cosmos DB への MongoDB アプリケーションの接続」で説明したように、ESHOP_AZURE_COSMOSDB 行をコメント解除し、Azure portal から取得した Azure Cosmos DB 接続文字列を使用してそれを更新する必要があります。
ESHOP_AZURE_COSMOSDB
グローバル変数が空の場合 (つまり、.env
ファイルでコメントアウトされている場合)、コンテナーでは既定の MongoDB 接続文字列が使用されます。 この接続文字列は、次の .yml コードに示すように、nosqldata
という名前の eShopOnContainers に配置されたローカル MongoDB コンテナーをポイントし、docker-compose ファイルに定義されていました。
# docker-compose.yml
version: '3.4'
services:
# ...Other services...
nosqldata:
image: mongo
その他の技術情報
NoSQL データベースのドキュメント データのモデリング
https://learn.microsoft.com/azure/cosmos-db/modeling-dataVaughn Vernon。 理想的なドメイン駆動設計集計ストアとは?
https://kalele.io/blog-posts/the-ideal-domain-driven-design-aggregate-store/Azure Cosmos DB の概要: MongoDB の API
https://learn.microsoft.com/azure/cosmos-db/mongodb-introductionAzure Cosmos DB:.NET および Azure portal を使用して MongoDB API の Web アプリを構築する
https://learn.microsoft.com/azure/cosmos-db/create-mongodb-dotnetローカルの開発とテストでの Azure Cosmos DB Emulator の使用
https://learn.microsoft.com/azure/cosmos-db/local-emulatorAzure Cosmos DB への MongoDB アプリケーションの接続
https://learn.microsoft.com/azure/cosmos-db/connect-mongodb-accountMongoDB Docker イメージ (Linux と Windows コンテナー)
https://hub.docker.com/_/mongo/Azure Cosmos DB:MongoDB API アカウントでの Studio 3T の使用
https://learn.microsoft.com/azure/cosmos-db/mongodb-mongochef
.NET