Ha az alkalmazáskódnak tudnia kell, hogy MongoDB vagy Cassandra van mögötte, az tervezési hiba. Nem feltétlenül katasztrófa, de hiba, mert az üzleti logika így egy infrastruktúra-döntéstől függ. A PolyPersist .NET-ben választja le a kettőt: egységes interfészeket ad dokumentum-, oszlop- és blob tárak fölé.

Mi a probléma pontosan?

Az ORM-ek ezt részben megoldják, de csak SQL adatbázisokra. Ha egy projekt MongoDB-t használ a dokumentumokhoz, Cassandrát az idősoros adatokhoz és S3-at a fájlokhoz, mindegyikhez külön hozzáférési réteget kell írni. Ezek a rétegek nem kompatibilisek és nem cserélhetők egymásra, és minden projekt újraírja őket.

A cloud-agnosztikus fejlesztés ráadásul azt kéri, hogy az üzleti logikába ne égjen bele Azure- vagy AWS-függőség. Ezzel egyetértek, de megvalósítani csak egységes interfészekkel lehet, és a PolyPersist ezeket adja.

Hogyan működik?

A könyvtár három store típust absztrahál:

  • DocumentStore: MongoDB, in-memory (fejlesztéshez, teszteléshez)
  • ColumnStore: Cassandra, Amazon Redshift, Google BigQuery
  • BlobStore: Azure Blob, Amazon S3, Google Cloud Storage, MinIO, GridFS, FileSystem

A fogyasztó kód csak az interfészeket látja (IDocumentStore, IBlobStore, IColumnStore). Hogy mögöttük MongoDB vagy in-memory implementáció fut, azt a dependency injection konfigurációja dönti el, nem a kód. Így teszteléskor in-memory, élesben MongoDB futhat, és ehhez senkinek nem kell az üzleti logikához nyúlnia.

Multiplatform interfészek

A PolyPersist nem csak C#-ban létezik. Az összes store interfész definícióját a UniContract generálja, ezért az IDocumentStore, az IBlobStore és az IColumnStore contract C#-ban, Java-ban és Python-ban pontosan ugyanaz. Ha egy interfész változik, mindhárom nyelven egyszerre változik.

Egy Java-ban írt service tehát ugyanazzal a contracttal dolgozik, mint egy C# microservice, és a két definíciót senkinek nem kell kézzel szinkronban tartania. Az egyezést a toolchain biztosítja, nem a fejlesztők fegyelme. Ugyanez a generálás tartja egymással összhangban a C#, a Java és a Python implementációt is.

Tervezési döntések, amelyek számítanak

Az ETag alapú optimista zárolás minden entitásnál és minden adatbázisnál ugyanúgy működik. Ezt nem kapjuk ingyen, mert a Cassandra és a MongoDB egészen másképp kezeli a konkurenciát. Az egységesítés kompromisszum, de a fogyasztó kód szempontjából szerintem ez a helyes döntés.

Minden entitáson van PartitionKey. A Cosmos DB- és a DynamoDB-kompatibilitás miatt kell, és ha egy adatbázisnál nincs rá szükség, egyszerűen üresen marad. Jobb, ha ott van és nem kell, mint ha kellene, de hiányzik.

A tervezés async-first: minden művelet Task-alapú. Az adatbázis-hozzáférés mindig I/O-kötött, ezért a szinkron API hosszabb távon csak gondot okoz.

Mire jó ez a gyakorlatban?

Három helyzetben használom: ha el akarom kerülni a cloud provider lock-int az adatkezelési rétegben; ha a különböző projektek különböző adatbázisokat használnak, de a fejlesztőknek mindenhol ugyanazt az API-t szeretném adni; vagy ha a tesztekben in-memory implementációval, élesben valódi adatbázissal akarok dolgozni.

Az adatbázisok között vannak különbségek, és ezek számítanak. A helyük viszont az implementációs rétegben van, az üzleti logikának nem kell tudnia róluk. A PolyPersist erre az elvre épül.