Použití databází NoSQL jako infrastruktury trvalosti
Tip
Tento obsah je výňatek z eBooku, architektury mikroslužeb .NET pro kontejnerizované aplikace .NET, které jsou k dispozici na .NET Docs nebo jako zdarma ke stažení PDF, které lze číst offline.
Při použití databází NoSQL pro datovou vrstvu infrastruktury obvykle nepoužíváte ORM, jako je Entity Framework Core. Místo toho použijete rozhraní API poskytované modulem NoSQL, jako jsou Azure Cosmos DB, MongoDB, Cassandra, RavenDB, CouchDB nebo tabulky Azure Storage.
Pokud ale používáte databázi NoSQL, zejména databázi zaměřenou na dokumenty, jako je Azure Cosmos DB, CouchDB nebo RavenDB, způsob návrhu modelu s agregacemi DDD je částečně podobný tomu, jak to můžete udělat v EF Core, pokud jde o identifikaci agregovaných kořenových certifikátů, podřízených tříd entit a tříd objektů hodnot. Výběr databáze ale nakonec ovlivní váš návrh.
Při použití databáze orientované na dokumenty implementujete agregaci jako jeden dokument serializovaný ve formátu JSON nebo jiném formátu. Použití databáze je však z hlediska kódu doménového modelu transparentní. Při použití databáze NoSQL stále používáte třídy entit a agregujte kořenové třídy, ale s větší flexibilitou než při použití EF Core, protože trvalost není relační.
Rozdíl spočívá v tom, jak tento model přetrvává. Pokud jste implementovali svůj doménový model založený na třídách entit POCO, které jsou nezávislé na trvalosti infrastruktury, může to vypadat, jako byste mohli přejít na jinou infrastrukturu trvalosti, a to i z relačních do NoSQL. To by ale nemělo být vaším cílem. V různých databázových technologiích jsou vždy omezení a kompromisy, takže nebudete moct mít stejný model pro relační databáze nebo databáze NoSQL. Změna modelů trvalosti není triviální úlohou, protože transakce a operace trvalosti budou velmi odlišné.
Například v databázi orientované na dokumenty je v pořádku, aby agregační kořen měl více podřízených vlastností kolekce. V relační databázi není dotazování více podřízených vlastností kolekce snadno optimalizováno, protože z EF získáte příkaz UNION ALL SQL. Mít stejný doménový model pro relační databáze nebo databáze NoSQL není jednoduchý a neměli byste se to pokoušet. Model je opravdu potřeba navrhnout s pochopením, jak se budou data používat v každé konkrétní databázi.
Výhodou při použití databází NoSQL je, že entity jsou denormalizované, takže nenastavíte mapování tabulky. Váš doménový model může být flexibilnější než při použití relační databáze.
Při návrhu doménového modelu na základě agregací může být přechod na databáze NoSQL a databáze orientované na dokumenty ještě jednodušší než použití relační databáze, protože agregace, které navrhujete, jsou podobné serializovaným dokumentům v databázi orientované na dokumenty. Pak můžete do těchto "pytlů" zahrnout všechny informace, které pro tuto agregaci potřebujete.
Následující kód JSON je například ukázkovou implementací agregace objednávek při použití databáze orientované na dokument. Je to podobné agregaci objednávek, které jsme implementovali v ukázce eShopOnContainers, ale bez použití EF Core pod ní.
{
"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}
]
}
Úvod do služby Azure Cosmos DB a nativního rozhraní API služby Cosmos DB
Azure Cosmos DB je globálně distribuovaná databázová služba Microsoftu pro klíčové aplikace. Azure Cosmos DB poskytuje globální distribuci na klíč, elastické škálování propustnosti a úložiště po celém světě, latence v řádu milisekund na 99. percentilu, pět jasně definovaných úrovní konzistence a zaručenou vysokou dostupnost. To vše je podloženo nejlepšími smlouvami SLA v oboru. Azure Cosmos DB automaticky indexuje data, aniž by vyžadovala zapojení správy schémat a indexů. Zahrnuje více modelů a podporuje modely dokumentů, klíčových hodnot, grafů a sloupcových dat.
Obrázek 7–19 Globální distribuce služby Azure Cosmos DB
Pokud k implementaci agregace používané rozhraním API služby Azure Cosmos DB použijete model jazyka C#, může být agregace podobná třídám POCO jazyka C# používaným s EF Core. Rozdíl je v tom, jak je používat z aplikačních a infrastrukturových vrstev, jako v následujícím kódu:
// 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);
Vidíte, že způsob, jakým pracujete s doménovým modelem, může být podobný způsobu, jakým ho používáte ve vrstvě doménového modelu, když je infrastruktura EF. Stále používáte stejné agregační kořenové metody k zajištění konzistence, invariantů a ověření v rámci agregace.
Pokud ale model zachováte do databáze NoSQL, kód a rozhraní API se výrazně v porovnání s kódem EF Core nebo jiným kódem souvisejícím s relačními databázemi výrazně změní.
Implementace kódu .NET zaměřeného na MongoDB a Azure Cosmos DB
Použití služby Azure Cosmos DB z kontejnerů .NET
K databázím Azure Cosmos DB můžete přistupovat z kódu .NET spuštěného v kontejnerech, například z jakékoli jiné aplikace .NET. Například mikroslužby Locations.API a Marketing.API v eShopOnContainers jsou implementované, aby mohly využívat databáze Azure Cosmos DB.
Ve službě Azure Cosmos DB ale platí omezení z hlediska vývojového prostředí Dockeru. I když existuje místní emulátor služby Azure Cosmos DB, který může běžet na místním vývojovém počítači, podporuje pouze Windows. Linux a macOS se nepodporují.
Existuje také možnost spustit tento emulátor v Dockeru, ale jenom v kontejnerech Windows, ne s kontejnery Linuxu. To je počáteční handicap pro vývojové prostředí, pokud je vaše aplikace nasazená jako kontejnery Linuxu, protože v současné době nemůžete nasadit linuxové a windows kontejnery ve službě Docker pro Windows současně. Všechny nasazené kontejnery musí být určené pro Linux nebo pro Windows.
Ideální a jednodušší nasazení pro řešení pro vývoj/testování je možnost nasadit vaše databázové systémy jako kontejnery spolu s vlastními kontejnery, aby prostředí pro vývoj a testování byla vždy konzistentní.
Použití rozhraní MongoDB API pro místní vývoj/testování linuxových nebo windows kontejnerů a azure Cosmos DB
Databáze Cosmos DB podporují rozhraní MongoDB API pro .NET i nativní wire protokol MongoDB. To znamená, že pomocí existujících ovladačů teď vaše aplikace napsaná pro MongoDB může komunikovat se službou Cosmos DB a používat databáze Cosmos DB místo databází MongoDB, jak je znázorněno na obrázku 7–20.
Obrázek 7–20 Použití rozhraní MongoDB API a protokolu pro přístup ke službě Azure Cosmos DB
Jedná se o velmi pohodlný přístup k testování konceptů v prostředíCh Dockeru s kontejnery Linuxu, protože image Dockeru MongoDB Docker je multi-archová image, která podporuje kontejnery Docker Linuxu a kontejnery Dockeru Pro Windows.
Jak je znázorněno na následujícím obrázku, pomocí rozhraní MongoDB API podporuje eShopOnContainers kontejnery MongoDB Linux a Windows pro místní vývojové prostředí, ale pak můžete přejít na škálovatelné cloudové řešení PaaS jako Azure Cosmos DB tak, že jednoduše změníte připojovací řetězec MongoDB tak, aby odkazovat na Azure Cosmos DB.
Obrázek 7–21 eShopOnContainers s využitím kontejnerů MongoDB pro vývoj a vývoj nebo Azure Cosmos DB pro produkční prostředí
Produkční služba Azure Cosmos DB by běžela v cloudu Azure jako služba PaaS a škálovatelná služba.
Vaše vlastní kontejnery .NET můžou běžet na místním vývojovém hostiteli Dockeru (který používá Docker pro Windows na počítači s Windows 10) nebo je nasadit do produkčního prostředí, jako je Kubernetes v Azure AKS nebo Azure Service Fabric. V tomto druhém prostředí byste nasadí pouze vlastní kontejnery .NET, ale ne kontejner MongoDB, protože byste k zpracování dat v produkčním prostředí používali Službu Azure Cosmos DB v cloudu.
Jasnou výhodou použití rozhraní MongoDB API je, že vaše řešení by mohlo běžet v obou databázových strojích, MongoDB nebo Azure Cosmos DB, takže migrace do různých prostředí by měla být snadná. Někdy je ale vhodné použít nativní rozhraní API (které je nativní rozhraní API služby Cosmos DB), abyste plně využili výhod funkcí konkrétního databázového stroje.
Další porovnání mezi jednoduchým používáním MongoDB a Cosmos DB v cloudu najdete v výhodách používání služby Azure Cosmos DB na této stránce.
Analýza přístupu pro produkční aplikace: Rozhraní MongoDB API vs. rozhraní API služby Cosmos DB
EShopOnContainers používá rozhraní MongoDB API, protože prioritou bylo mít konzistentní vývojové/testovací prostředí pomocí databáze NoSQL, která by mohla pracovat také se službou Azure Cosmos DB.
Pokud ale plánujete používat rozhraní MongoDB API pro přístup ke službě Azure Cosmos DB v Azure pro produkční aplikace, měli byste analyzovat rozdíly v možnostech a výkonu při použití rozhraní MongoDB API pro přístup k databázím Azure Cosmos DB ve srovnání s použitím nativního rozhraní API služby Azure Cosmos DB. Pokud je to podobné, můžete použít rozhraní MongoDB API a získat výhodu podpory dvou databázových strojů NoSQL najednou.
Clustery MongoDB můžete také používat jako produkční databázi v cloudu Azure s MongoDB Atlas v Microsoft Azure. To ale není služba PaaS poskytovaná Microsoftem. V tomto případě Azure právě hostuje toto řešení pocházející z MongoDB.
V podstatě jde jen o upozornění, že byste neměli vždy používat rozhraní API MongoDB proti Azure Cosmos DB. eShopOnContainers ho používá, protože to byla vhodná volba pro Linuxové kontejnery. Rozhodnutí by mělo být založeno na konkrétních potřebách a testech, které musíte udělat pro produkční aplikaci.
Kód: Použití rozhraní MongoDB API v aplikacích .NET
Rozhraní MongoDB API pro .NET je založené na balíčcích NuGet, které potřebujete přidat do svých projektů, například v projektu Locations.API znázorněné na následujícím obrázku.
Obrázek 7–22 Odkazy na balíčky NuGet rozhraní MongoDB API v projektu .NET
Pojďme prozkoumat kód v následujících částech.
Model používaný rozhraním MongoDB API
Nejprve musíte definovat model, který bude obsahovat data pocházející z databáze v paměťovém prostoru vaší aplikace. Tady je příklad modelu použitého pro umístění v eShopOnContainers.
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)));
}
}
Můžete si prohlédnout několik atributů a typů pocházejících z balíčků NuGet MongoDB.
Databáze NoSQL jsou obvykle velmi vhodné pro práci s nerelačními hierarchickými daty. V tomto příkladu používáme typy MongoDB, zejména pro geografická umístění, například GeoJson2DGeographicCoordinates
.
Načtení databáze a kolekce
V eShopOnContainers jsme vytvořili vlastní kontext databáze, kde implementujeme kód pro načtení databáze a MongoCollections, stejně jako v následujícím kódu.
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");
}
}
}
Načtení dat
V kódu jazyka C#, jako jsou kontrolery webového rozhraní API nebo implementace vlastních úložišť, můžete při dotazování prostřednictvím rozhraní MongoDB API napsat podobný kód jako následující. Všimněte si, že _context
objekt je instance předchozí LocationsContext
třídy.
public async Task<Locations> GetAsync(int locationId)
{
var filter = Builders<Locations>.Filter.Eq("LocationId", locationId);
return await _context.Locations
.Find(filter)
.FirstOrDefaultAsync();
}
Použití env-var v souboru docker-compose.override.yml pro připojovací řetězec MongoDB
Při vytváření objektu MongoClient potřebuje základní parametr, což je přesně ConnectionString
parametr odkazující na správnou databázi. V případě eShopOnContainers může připojovací řetězec odkazovat na místní kontejner Docker MongoDB nebo na "produkční" databázi Azure Cosmos DB. Tato připojovací řetězec pochází z proměnných prostředí definovaných v docker-compose.override.yml
souborech používaných při nasazování pomocí docker-compose nebo sady Visual Studio, jako v následujícím kódu yml.
# docker-compose.override.yml
version: '3.4'
services:
# Other services
locations-api:
environment:
# Other settings
- ConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosqldata}
Důležité
Microsoft doporučuje používat nejbezpečnější dostupný tok ověřování. Pokud se připojujete k Azure SQL, spravované identity pro prostředky Azure se doporučují metodou ověřování.
Proměnná ConnectionString
prostředí se vyřeší tímto způsobem: Pokud ESHOP_AZURE_COSMOSDB
je globální proměnná definovaná v .env
souboru s připojovací řetězec Azure Cosmos DB, použije ji pro přístup k databázi Azure Cosmos DB v cloudu. Pokud není definovaný, převezme mongodb://nosqldata
hodnotu a použije vývojový kontejner MongoDB.
Následující kód ukazuje .env
soubor se službou Azure Cosmos DB připojovací řetězec globální proměnnou prostředí, jak je implementováno v eShopOnContainers:
# .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>
Odkomentujte řádek ESHOP_AZURE_COSMOSDB a aktualizujte ho pomocí připojovací řetězec Azure Cosmos DB získané z webu Azure Portal, jak je vysvětleno v článku Připojení aplikace MongoDB ke službě Azure Cosmos DB.
ESHOP_AZURE_COSMOSDB
Pokud je globální proměnná prázdná, což znamená, že je v .env
souboru zakomentovaná, kontejner použije výchozí připojovací řetězec MongoDB. Tato připojovací řetězec odkazuje na místní kontejner MongoDB nasazený v eShopOnContainers s názvem nosqldata
a byl definován v souboru docker-compose, jak je znázorněno v následujícím .yml kódu:
# docker-compose.yml
version: '3.4'
services:
# ...Other services...
nosqldata:
image: mongo
Další materiály
Modelování dokumentových dat pro databáze NoSQL
https://learn.microsoft.com/azure/cosmos-db/modeling-dataVaughn Vernon. Ideální úložiště agregace návrhu řízené doménou?
https://kalele.io/blog-posts/the-ideal-domain-driven-design-aggregate-store/Úvod do služby Azure Cosmos DB: ROZHRANÍ API pro MongoDB
https://learn.microsoft.com/azure/cosmos-db/mongodb-introductionAzure Cosmos DB: Vytvoření webové aplikace rozhraní MongoDB API pomocí .NET a webu Azure Portal
https://learn.microsoft.com/azure/cosmos-db/create-mongodb-dotnetPoužití emulátoru služby Azure Cosmos DB pro místní vývoj a testování
https://learn.microsoft.com/azure/cosmos-db/local-emulatorPřipojení aplikace MongoDB ke službě Azure Cosmos DB
https://learn.microsoft.com/azure/cosmos-db/connect-mongodb-accountImage Dockeru MongoDB (Linux a kontejner Windows)
https://hub.docker.com/_/mongo/Použití MongoChef (Studio 3T) s účtem rozhraní API pro MongoDB
https://learn.microsoft.com/azure/cosmos-db/mongodb-mongochef