During the last decade or so, the most popular databases have been relational database management systems. Hence, there is a huge number of developers who know how to build an application that requires the persisting and querying of data by creating tables and relationships in relational databases, such as Microsoft SQL Server.
However, when we work with C# and .NET Core, we work with object-oriented programming. LINQ makes it possible to easily query objects by adding functional programming features to C#, but we need to add a complexity layer between our application and the relational databases: an Object-Relational Mapping (ORM) solution, such as Entity Framework or NHibernate.
We have entities in our C# application, and the ORM maps these entities' relationships to the tables. This way, we can create an instance of an entity and persist it in the underlying tables in the relational database system. The ORM translates the operations into the necessary SQL for the relational database system to insert new rows in the appropriate tables.
The first version of an application that works with C#, an ORM, and a relational database is not a problem. However, when new requirements arrive and we need to add new properties to an existing object or relate it to another object, we have to perform migrations to make it possible to use the new objects in the different operations and persist them in the underlying relational database. We have to make changes in different places. We need to edit the classes that define the properties that an entity has to persist, we have to make sure that the ORM mappings are updated, and we need to ensure that the underlying relational database has the new columns in the necessary tables. Hence, we make changes in the code, in the ORM, and in the underlying database schema.
Whenever it is necessary to deploy a new version of the application, we have to make sure that the migration process is executed and that the underlying database schema has the required version for the C# code and the ORM configuration. The migration process makes the necessary changes in the tables and relationships to make it match the ORM mappings. Hence, a single property that needs to be added to an object and needs to be persisted generates a cascade of changes in different parts of our application. Of course, there are many ways of automating the necessary tasks. However, the fact that the tasks are automated doesn't mean that they aren't required.
Now, let's start thinking about the way in which we are going to work with a Cosmos DB NoSQL document database. We can start writing our first version of an application with C# and .NET Core, work with object-oriented programming, and use the provided methods in the Cosmos DB .NET Core SDK to persist the created objects in the schema-agnostic document database. There is no ORM between our application code and the NoSQL database service. We work with objects, we persist them, we retrieve them, and we query them. We only need to specify serialization and deserialization settings if necessary, but we don't have to worry about mapping an object to tables and their relationships.
Now, seriously, what else do we need to do to create our first version of the application? We have to learn how to work with the Cosmos DB .NET Core SDK, as well as the necessary tools for interacting with and managing a Cosmos DB database, in order to be ready for the first version of the application. We also have to understand the SQL dialect, which allows us to work against a Cosmos DB document database, in addition to many scalability and provisioning strategies.
Whenever it is necessary to deploy a new version of an application that is working against a Cosmos DB NoSQL document database, we don't have to worry about migration processes. If we need to add a single property to an object, we just add it and persist it in the schema-agnostic document database. There is no need to run any script that makes changes to the existing documents. We can continue working with the documents with a different schema, as they will be able to coexist with the new documents that have a new schema.
What about queries that work only with the new property? No problem—we can use properties or keys that don't exist in all the persisted documents; the schema-agnostic features support this scenario. For example, we can start running queries that check whether the value of the new property matches some specific criteria after persisting an object that has the new property.
You might be wondering, "why haven't I been working with NoSQL for the last 10 years?" There is a simple answer to this question: storage costs were higher and relational database management systems made it easy to optimize storage use while providing great database features. However, things have changed, and nowadays, we have new options. Cosmos DB provides a NoSQL database service that allows us to get up and running very quickly. We will learn how to create a first version and a second version of an application to simplify the paradigm shift to the NoSQL way of working with Cosmos DB.
Obviously, as always happens, relational databases will still be great for thousands of scenarios. However, be sure that the time you invest in learning Cosmos DB features will allow you to use its services in an application in which you thought that the only choice was a traditional relational database.