NoSQL-databases gebruiken als persistentie-infrastructuur
Tip
Deze inhoud is een fragment uit het eBook, .NET Microservices Architecture for Containerized .NET Applications, beschikbaar op .NET Docs of als een gratis downloadbare PDF die offline kan worden gelezen.
Wanneer u NoSQL-databases gebruikt voor uw infrastructuurgegevenslaag, gebruikt u doorgaans geen ORM zoals Entity Framework Core. In plaats daarvan gebruikt u de API van de NoSQL-engine, zoals Azure Cosmos DB, MongoDB, Cassandra, RavenDB, CouchDB of Azure Storage Tables.
Wanneer u echter een NoSQL-database gebruikt, met name een documentgerichte database zoals Azure Cosmos DB, CouchDB of RavenDB, is de manier waarop u uw model ontwerpt met DDD-aggregaties gedeeltelijk vergelijkbaar met de manier waarop u dit kunt doen in EF Core, met betrekking tot de identificatie van statistische wortels, onderliggende entiteitsklassen en waardeobjectklassen. Maar uiteindelijk heeft de databaseselectie invloed op uw ontwerp.
Wanneer u een documentgeoriënteerde database gebruikt, implementeert u een aggregaties als één document, geserialiseerd in JSON of een andere indeling. Het gebruik van de database is echter transparant vanuit het oogpunt van een domeinmodelcode. Wanneer u een NoSQL-database gebruikt, gebruikt u nog steeds entiteitsklassen en statistische hoofdklassen, maar met meer flexibiliteit dan wanneer u EF Core gebruikt, omdat de persistentie niet relationeel is.
Het verschil is hoe u dat model persistent maakt. Als u uw domeinmodel hebt geïmplementeerd op basis van POCO-entiteitsklassen, onafhankelijk van de persistentie van de infrastructuur, lijkt het erop dat u naar een andere persistentie-infrastructuur kunt gaan, zelfs van relationeel naar NoSQL. Dat mag echter niet uw doel zijn. Er zijn altijd beperkingen en afwegingen in de verschillende databasetechnologieën, dus u kunt niet hetzelfde model hebben voor relationele of NoSQL-databases. Het wijzigen van persistentiemodellen is geen triviale taak, omdat transacties en persistentiebewerkingen heel anders zijn.
In een documentgeoriënteerde database is het bijvoorbeeld in orde dat een statistische hoofdmap meerdere onderliggende verzamelingseigenschappen heeft. In een relationele database is het uitvoeren van query's op meerdere onderliggende verzamelingseigenschappen niet eenvoudig geoptimaliseerd, omdat u een UNION ALL SQL-instructie van EF krijgt. Het hebben van hetzelfde domeinmodel voor relationele databases of NoSQL-databases is niet eenvoudig en u moet dit niet proberen. U moet uw model echt ontwerpen met inzicht in hoe de gegevens in elke specifieke database worden gebruikt.
Een voordeel bij het gebruik van NoSQL-databases is dat de entiteiten meer gedenormaliseerd zijn, zodat u geen tabeltoewijzing instelt. Uw domeinmodel kan flexibeler zijn dan wanneer u een relationele database gebruikt.
Wanneer u uw domeinmodel ontwerpt op basis van aggregaties, is het verplaatsen naar NoSQL en documentgeoriënteerde databases mogelijk nog eenvoudiger dan het gebruik van een relationele database, omdat de aggregaties die u ontwerpt vergelijkbaar zijn met geserialiseerde documenten in een documentgerichte database. Vervolgens kunt u deze 'zakken' opnemen in alle informatie die u mogelijk nodig hebt voor die aggregaties.
De volgende JSON-code is bijvoorbeeld een voorbeeld-implementatie van een orderaggregaties bij het gebruik van een documentgeoriënteerde database. Het is vergelijkbaar met de orderaggregaties die we hebben geïmplementeerd in het voorbeeld eShopOnContainers, maar zonder ef Core eronder te gebruiken.
{
"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}
]
}
Inleiding tot Azure Cosmos DB en de systeemeigen Cosmos DB-API
Azure Cosmos DB is de wereldwijd gedistribueerde databaseservice van Microsoft voor bedrijfskritieke toepassingen. Azure Cosmos DB biedt gebruiksklare wereldwijde distributie, elastisch schalen van doorvoer en opslag, latentie die wereldwijd in het 99e percentiel onder de 10 milliseconden blijft, vijf goed gedefinieerde consistentieniveaus en een gegarandeerd hoge beschikbaarheid, allemaal op basis van toonaangevende serviceovereenkomsten. Met Azure Cosmos DB worden gegevens automatisch geïndexeerd, zonder dat u te maken krijgt met schema- en indexbeheer. Het beschikt over meerdere modellen en ondersteunt modellen voor document-, sleutelwaarde-, grafiek- en kolomgegevens.
Afbeelding 7-19. Globale distributie met Azure DB Cosmos
Wanneer u een C#-model gebruikt om de statistische functie te implementeren die moet worden gebruikt door de Azure Cosmos DB-API, kan de statistische functie vergelijkbaar zijn met de C#-POCO-klassen die worden gebruikt met EF Core. Het verschil is in de manier om ze te gebruiken vanuit de toepassings- en infrastructuurlagen, zoals in de volgende code:
// 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);
U kunt zien dat de manier waarop u met uw domeinmodel werkt, vergelijkbaar kan zijn met de manier waarop u dit gebruikt in uw domeinmodellaag wanneer de infrastructuur EF is. U gebruikt nog steeds dezelfde statistische basismethoden om consistentie, invarianten en validaties in de statistische functie te garanderen.
Wanneer u uw model echter ophoudt in de NoSQL-database, worden de code en API aanzienlijk gewijzigd in vergelijking met EF Core-code of andere code die betrekking heeft op relationele databases.
.NET-code implementeren die is gericht op MongoDB en Azure Cosmos DB
Azure Cosmos DB gebruiken vanuit .NET-containers
U hebt toegang tot Azure Cosmos DB-databases vanuit .NET-code die wordt uitgevoerd in containers, zoals elke andere .NET-toepassing. De microservices Locations.API en Marketing.API in eShopOnContainers worden bijvoorbeeld geïmplementeerd, zodat ze Azure Cosmos DB-databases kunnen gebruiken.
Er is echter een beperking in Azure Cosmos DB vanuit het oogpunt van een Docker-ontwikkelomgeving. Hoewel er een on-premises Azure Cosmos DB Emulator is die kan worden uitgevoerd op een lokale ontwikkelcomputer, ondersteunt deze alleen Windows. Linux en macOS worden niet ondersteund.
Er is ook de mogelijkheid om deze emulator uit te voeren op Docker, maar alleen in Windows-containers, niet met Linux-containers. Dat is een eerste handicap voor de ontwikkelomgeving als uw toepassing wordt geïmplementeerd als Linux-containers, omdat u op dit moment geen Linux- en Windows-containers kunt implementeren in Docker voor Windows. Alle containers die worden geïmplementeerd, moeten voor Linux of voor Windows zijn.
De ideale en eenvoudigere implementatie voor een dev/test-oplossing is om uw databasesystemen als containers samen met uw aangepaste containers te implementeren, zodat uw ontwikkel-/testomgevingen altijd consistent zijn.
MongoDB-API gebruiken voor lokale dev/test Linux/Windows-containers plus Azure Cosmos DB
Cosmos DB-databases ondersteunen MongoDB-API voor .NET en het systeemeigen MongoDB-wire-protocol. Dit betekent dat uw toepassing die is geschreven voor MongoDB nu kan communiceren met Cosmos DB en cosmos DB-databases kan gebruiken in plaats van MongoDB-databases, zoals wordt weergegeven in afbeelding 7-20.
Afbeelding 7-20. MongoDB-API en -protocol gebruiken voor toegang tot Azure Cosmos DB
Dit is een zeer handige benadering voor het testen van concepten in Docker-omgevingen met Linux-containers, omdat de MongoDB Docker-installatiekopieën een installatiekopieën met meerdere boog is die ondersteuning biedt voor Docker Linux-containers en Docker Windows-containers.
Zoals wordt weergegeven in de volgende afbeelding, ondersteunt eShopOnContainers met behulp van de MongoDB-API MongoDB Linux- en Windows-containers voor de lokale ontwikkelomgeving, maar vervolgens kunt u overstappen op een schaalbare PaaS-cloudoplossing als Azure Cosmos DB door simpelweg de MongoDB-verbindingsreeks te wijzigen zodat deze verwijst naar Azure Cosmos DB.
Afbeelding 7-21. eShopOnContainers met MongoDB-containers voor dev-env of Azure Cosmos DB voor productie
De productie van Azure Cosmos DB wordt uitgevoerd in de cloud van Azure als paaS en schaalbare service.
Uw aangepaste .NET-containers kunnen worden uitgevoerd op een lokale Docker-host voor ontwikkeling (die Docker voor Windows op een Windows 10-computer gebruikt) of worden geïmplementeerd in een productieomgeving, zoals Kubernetes in Azure AKS of Azure Service Fabric. In deze tweede omgeving implementeert u alleen de aangepaste .NET-containers, maar niet de MongoDB-container, omdat u Azure Cosmos DB in de cloud zou gebruiken voor het verwerken van de gegevens in productie.
Een duidelijk voordeel van het gebruik van de MongoDB-API is dat uw oplossing kan worden uitgevoerd in zowel database-engines, MongoDB als Azure Cosmos DB, zodat migraties naar verschillende omgevingen eenvoudig moeten zijn. Soms is het echter de moeite waard om een systeemeigen API (dat is de systeemeigen Cosmos DB-API) te gebruiken om optimaal te profiteren van de mogelijkheden van een specifieke database-engine.
Zie de voordelen van het gebruik van Azure Cosmos DB op deze pagina voor een verdere vergelijking tussen het gebruik van MongoDB en Cosmos DB in de cloud.
Analyseer uw benadering voor productietoepassingen: MongoDB-API versus Cosmos DB-API
eShopOnContainers maakt gebruik van MongoDB-API omdat de prioriteit fundamenteel was om een consistente ontwikkel-/testomgeving te hebben met behulp van een NoSQL-database die ook kon werken met Azure Cosmos DB.
Als u echter van plan bent mongoDB-API te gebruiken voor toegang tot Azure Cosmos DB in Azure voor productietoepassingen, moet u de verschillen in mogelijkheden en prestaties analyseren bij het gebruik van MongoDB-API voor toegang tot Azure Cosmos DB-databases in vergelijking met het gebruik van de systeemeigen Azure Cosmos DB-API. Als dit vergelijkbaar is, kunt u mongoDB-API gebruiken en tegelijkertijd twee NoSQL-database-engines ondersteunen.
U kunt ook MongoDB-clusters gebruiken als de productiedatabase in de cloud van Azure, met MongoDB Atlas op Microsoft Azure. Maar dat is geen PaaS-service die wordt geleverd door Microsoft. In dit geval host Azure alleen die oplossing die afkomstig is van MongoDB.
In principe is dit slechts een disclaimer om te zeggen dat u niet altijd MongoDB-API moet gebruiken voor Azure Cosmos DB. eShopOnContainers gebruikt het omdat het een handige keuze was voor Linux-containers. De beslissing moet zijn gebaseerd op de specifieke behoeften en tests die u moet uitvoeren voor uw productietoepassing.
De code: MongoDB-API gebruiken in .NET-toepassingen
De MongoDB-API voor .NET is gebaseerd op NuGet-pakketten die u aan uw projecten moet toevoegen, zoals in het project Locations.API dat wordt weergegeven in de volgende afbeelding.
Afbeelding 7-22. Verwijzingen naar MongoDB API NuGet-pakketten in een .NET-project
Laten we de code in de volgende secties onderzoeken.
Een model dat wordt gebruikt door mongoDB-API
Eerst moet u een model definiëren dat de gegevens bevat die afkomstig zijn van de database in de geheugenruimte van uw toepassing. Hier volgt een voorbeeld van het model dat wordt gebruikt voor Locaties bij 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)));
}
}
U kunt zien dat er enkele kenmerken en typen afkomstig zijn van de MongoDB NuGet-pakketten.
NoSQL-databases zijn meestal zeer geschikt voor het werken met niet-relationele hiërarchische gegevens. In dit voorbeeld gebruiken we MongoDB-typen die speciaal zijn gemaakt voor geografische locaties, zoals GeoJson2DGeographicCoordinates
.
De database en de verzameling ophalen
In eShopOnContainers hebben we een aangepaste databasecontext gemaakt waarin we de code implementeren om de database en de MongoCollections op te halen, zoals in de volgende code.
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");
}
}
}
De gegevens ophalen
In C#-code, zoals web-API-controllers of implementatie van aangepaste opslagplaatsen, kunt u vergelijkbare code schrijven als hieronder bij het uitvoeren van query's via de MongoDB-API.
_context
Het object is een exemplaar van de vorige LocationsContext
klasse.
public async Task<Locations> GetAsync(int locationId)
{
var filter = Builders<Locations>.Filter.Eq("LocationId", locationId);
return await _context.Locations
.Find(filter)
.FirstOrDefaultAsync();
}
Gebruik een env-var in het docker-compose.override.yml-bestand voor de MongoDB-verbindingsreeks
Bij het maken van een MongoClient-object heeft het een fundamentele parameter nodig die precies de ConnectionString
parameter is die verwijst naar de juiste database. In het geval van eShopOnContainers kan de verbindingsreeks verwijzen naar een lokale MongoDB Docker-container of naar een 'productie' Azure Cosmos DB-database. Die verbindingsreeks afkomstig is van de omgevingsvariabelen die zijn gedefinieerd in de bestanden die worden gebruikt bij het docker-compose.override.yml
implementeren met docker-compose of Visual Studio, zoals in de volgende yml-code.
# docker-compose.override.yml
version: '3.4'
services:
# Other services
locations-api:
environment:
# Other settings
- ConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosqldata}
Belangrijk
Microsoft raadt u aan de veiligste verificatiestroom te gebruiken die beschikbaar is. Als u verbinding maakt met Azure SQL, is Managed Identities voor Azure-resources de aanbevolen verificatiemethode.
De ConnectionString
omgevingsvariabele wordt op deze manier opgelost: als de ESHOP_AZURE_COSMOSDB
globale variabele is gedefinieerd in het .env
bestand met de Azure Cosmos DB-verbindingsreeks, wordt deze gebruikt voor toegang tot de Azure Cosmos DB-database in de cloud. Als deze niet is gedefinieerd, wordt de mongodb://nosqldata
waarde gebruikt en wordt de MongoDB-container voor ontwikkeling gebruikt.
De volgende code toont het .env
bestand met de globale omgevingsvariabele van Azure Cosmos DB verbindingsreeks, zoals geïmplementeerd in 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>
Verwijder opmerkingen bij de ESHOP_AZURE_COSMOSDB regel en werk deze bij met uw Azure Cosmos DB-verbindingsreeks verkregen via Azure Portal, zoals uitgelegd in Een MongoDB-toepassing verbinden met Azure Cosmos DB.
Als de ESHOP_AZURE_COSMOSDB
globale variabele leeg is, wat betekent dat deze als commentaar in het .env
bestand wordt opgenomen, gebruikt de container een standaard MongoDB-verbindingsreeks. Deze verbindingsreeks verwijst naar de lokale MongoDB-container die is geïmplementeerd in eShopOnContainers met de naam nosqldata
en die is gedefinieerd in het docker-compose-bestand, zoals wordt weergegeven in de volgende .yml code:
# docker-compose.yml
version: '3.4'
services:
# ...Other services...
nosqldata:
image: mongo
Aanvullende bronnen
Documentgegevens modelleren voor NoSQL-databases
https://learn.microsoft.com/azure/cosmos-db/modeling-dataVaughn Vernon. De ideale aggregatiesopslag voor domeingestuurd ontwerp?
https://kalele.io/blog-posts/the-ideal-domain-driven-design-aggregate-store/Inleiding tot Azure Cosmos DB: API voor MongoDB
https://learn.microsoft.com/azure/cosmos-db/mongodb-introductionAzure Cosmos DB: een MongoDB-API-web-app ontwikkelen met .NET en de Azure-portal
https://learn.microsoft.com/azure/cosmos-db/create-mongodb-dotnetDe Azure Cosmos DB Emulator gebruiken voor lokale ontwikkeling en testen
https://learn.microsoft.com/azure/cosmos-db/local-emulatorEen MongoDB-toepassing verbinden met Azure Cosmos DB
https://learn.microsoft.com/azure/cosmos-db/connect-mongodb-accountDe MongoDB Docker-installatiekopieën (Linux en Windows-container)
https://hub.docker.com/_/mongo/MongoChef (Studio 3T) gebruiken met een Azure Cosmos DB: API voor MongoDB-account
https://learn.microsoft.com/azure/cosmos-db/mongodb-mongochef