NoSQL Databases: Architecture, Data Models, and Practical Use

Understand why NoSQL databases emerged and how their architectures differ from relational systems. This tutorial explores document, key-value, wide-column, and graph databases; data modeling; partitioning; sharding; replication; consistency; CAP; transactions; caching; CQRS; microservices; and polyglot persistence. It concludes with practical architecture decisions for ownership, data flow, scaling, performance, and failure scenarios.

1. Lesson 1: Why NoSQL Exists Demo 2. Lesson 2: Relational vs. NoSQL: The Fundamental Differences Demo 3. Lesson 3: The Distributed Systems Foundations of NoSQL Demo 4. Lesson 4: Document Databases Demo 5. Lesson 5: Key-Value Databases Demo 6. Lesson 6: Wide-Column Databases Locked 7. Lesson 7: Graph Databases Locked 8. Lesson 8: Data Modeling in NoSQL Locked 9. Lesson 9: Partitioning and Sharding Locked 10. Lesson 10: Replication Locked 11. Lesson 11: Consistency Models Locked 12. Lesson 12: CAP Theorem in Practice Locked 13. Lesson 13: Transactions in NoSQL Locked 14. Lesson 14: ACID, BASE, and Eventual Consistency Locked 15. Lesson 15: Handling Relationships Without Relational Joins Locked 16. Lesson 16: MongoDB Architecture and Design Locked 17. Lesson 17: Redis Architecture and Use Cases Locked 18. Lesson 18: Cassandra Architecture and Data Modeling Locked 19. Lesson 19: DynamoDB Architecture and Design Locked 20. Lesson 20: Graph Databases with Neo4j Locked 21. Lesson 21: NoSQL in Microservices Locked 22. Lesson 22: NoSQL, Caching, CQRS, and Event-Driven Architecture Locked 23. Lesson 23: Choosing the Right Database: Architecture Decision Framework Locked 24. Lesson 24: Designing a Polyglot Persistence System Locked 25. Lesson 25: Assigning Data Ownership to Services Locked 26. Lesson 26: Designing the Database Architecture Locked 27. Lesson 27: Designing the Data Flow Locked 28. Lesson 28: Consistency, Replication, and Failure Scenarios Locked 29. Lesson 29: Scaling and Performance Architecture Locked 30. Lesson 30: Defending the Architecture: ADR and Architectural Trade-offs Locked

Lesson 3: The Distributed Systems Foundations of NoSQL

Demo

Many of the most important NoSQL concepts are actually distributed-systems concepts. Before going deeper into individual databases, this lesson builds an understanding of nodes, replication, partitioning, fault tolerance, availability, and network failures. These concepts explain why distributed databases behave differently from a database running on a single machine and why architectural trade-offs become unavoidable at scale.

Why Distributed Databases Exist

A distributed database stores and processes data across multiple machines, called nodes, instead of depending on a single database server. This approach becomes important when an application needs more capacity, higher availability, or geographic distribution than one machine can reasonably provide.

For example, imagine an online learning platform with millions of students. One database server may eventually become a bottleneck because it has limits on CPU, memory, storage, and network throughput. A distributed database can use several machines so that the workload and data are spread across the cluster.

Technologies such as MongoDB, Cassandra, DynamoDB, and Couchbase are designed with distributed operation as an important part of their architecture. Relational databases can also participate in distributed architectures, but many NoSQL systems were designed around these requirements from the beginning.

Nodes and Clusters

A node is an individual machine or database instance participating in a distributed database. Multiple nodes form a cluster, and the database software coordinates these nodes to provide storage and access to data.

Consider a MongoDB deployment with three nodes. Instead of thinking of the system as three independent databases, the application normally interacts with the cluster as a logical database. The database infrastructure determines which node should process a request and how data should be replicated.

This abstraction is useful for application developers, but architects need to understand what happens underneath. Requests travel over a network, nodes can fail, data may exist in multiple locations, and communication between nodes is not guaranteed to be instantaneous or reliable.

Replication

Replication means keeping multiple copies of data on different nodes. Its main purposes are improved availability and fault tolerance, and sometimes improved read performance.

For example, suppose a customer record exists on three database nodes:

Node A: Customer 101
Node B: Customer 101
Node C: Customer 101

If Node A fails, another node can potentially continue serving the data. The system therefore does not depend on a single physical machine.

MongoDB uses replica sets, while Cassandra uses a different replication architecture in which data is distributed and replicated across nodes. DynamoDB also automatically maintains replicated infrastructure across availability zones.

Replication introduces an important problem: keeping copies synchronized. If one node receives an update, the other nodes need to learn about it. This can introduce replication delay and creates important consistency decisions.

Partitioning and Sharding

Replication creates multiple copies of the same data, but it does not solve the problem of storing more data than one machine can handle. Partitioning divides data into separate pieces so that different nodes store different portions of the dataset.

For example, suppose an application has 100 million customers. Instead of storing every customer on one machine, the database might distribute customers across several nodes:

Node A -> Customers 1-25M
Node B -> Customers 25M-50M
Node C -> Customers 50M-75M
Node D -> Customers 75M-100M

This is commonly called sharding in database systems. The database needs a mechanism, usually a partition or shard key, to determine where a particular piece of data belongs.

A poor partition key can create a hot partition, where a disproportionate amount of traffic reaches one node. Therefore, choosing partition keys becomes an important architectural responsibility when working with distributed NoSQL systems.

Combining Partitioning and Replication

Large distributed databases commonly use both partitioning and replication. Partitioning determines which nodes own the data, while replication determines how many copies of that data exist.

For example:

Partition 1 -> Node A, Node B
Partition 2 -> Node B, Node C
Partition 3 -> Node C, Node D

The application might have hundreds of millions of records distributed across many partitions, while each partition has multiple replicas.

This architecture provides both scalability and resilience. However, it also increases system complexity because the database must coordinate distributed operations and maintain replicas while nodes are joining, leaving, or failing.

Fault Tolerance

A distributed system is designed with the assumption that failures will happen. A node can crash, a disk can fail, a server can lose network connectivity, or an entire availability zone can become unavailable.

Fault tolerance means that the system can continue operating despite some of these failures. Replication is one of the main mechanisms used to achieve this.

Imagine an e-commerce application where one database node suddenly stops responding. If the data exists only on that node, the application may become unavailable. If the data has replicas on other nodes, the system may continue operating.

However, fault tolerance is not free. More replicas require more storage, network traffic, and coordination. An architect therefore has to balance resilience against cost and complexity.

Availability

Availability describes the ability of a system to continue responding to requests when failures occur. A highly available database is designed so that individual failures do not necessarily make the entire system unavailable.

For example, suppose a database cluster has three replicas and one node fails. The remaining nodes may continue serving requests. This is significantly more resilient than having one database server with no redundancy.

However, availability is not simply a property of the database product. Application architecture also matters. If the application depends on a single database endpoint, network connection, or service instance, another component can still become a single point of failure.

Network Partitions

One of the most important concepts in distributed systems is the network partition. A network partition occurs when nodes are running but cannot communicate correctly with one another.

For example:

Node A  ───── Node B

        X

Node C

Node A and Node B can communicate, but the connection to Node C has failed. Node C may still be operational and may even receive requests from some clients.

This creates a difficult question: what should happen if Node A receives an update while Node C cannot communicate with it? Should Node A continue accepting requests? Should it reject requests to protect consistency? What happens when communication is restored?

These questions lead directly to the CAP theorem.

CAP Theorem

The CAP theorem states that a distributed system cannot guarantee both Consistency and Availability during a network Partition.

Consistency means that the system provides a defined view of the data so that clients do not observe conflicting states according to the system's consistency guarantee. Availability means that requests continue receiving responses. Partition tolerance means that the system continues operating despite communication failures between nodes.

The important point is that network partitions are unavoidable in distributed systems. Therefore, during a partition, an architecture must make trade-offs between consistency and availability.

A system that prioritizes consistency may reject requests when it cannot guarantee that the data is correct. A system that prioritizes availability may continue accepting requests and deal with synchronization or conflicts later.

A Concrete Example

Imagine a global ticket-booking system with database nodes in Toronto and London. A network failure temporarily separates the two locations.

A customer in Toronto attempts to purchase the last available seat while another customer in London does the same. If both locations continue accepting the transaction independently, the system could potentially sell the same seat twice.

The system could instead prioritize consistency and temporarily reject one or both operations until the nodes can communicate. This reduces availability but protects the correctness of the data.

For something less critical, such as a social-media like counter, temporary differences may be acceptable. The system can continue accepting likes and reconcile the data later. This prioritizes availability and can provide better user experience during failures.

Why Distributed Databases Force Trade-Offs

The central architectural lesson is that distribution creates trade-offs. Adding more machines gives us scalability and fault tolerance, but it also introduces network communication, replication delays, partitions, coordination, and more complicated failure scenarios.

A single-server database can often perform an operation locally and immediately. A distributed database may need to communicate with several machines before it can safely complete the same operation. Network communication is slower and less reliable than communication inside one machine.

This is why an architect should never evaluate a distributed NoSQL database only by asking, "How many requests per second can it handle?" The more important questions are: What happens when a node fails? What happens during a network partition? How is data partitioned? How many replicas exist? What consistency guarantee does the application need?

Architectural Perspective

NoSQL databases became strongly associated with distributed architectures because many modern applications need to operate across multiple machines and sometimes multiple regions. Their architectures commonly combine partitioning, replication, fault tolerance, and configurable consistency to achieve large-scale operation.

The key lesson is that scalability and availability come with complexity. As a technical lead or architect, you need to understand not only the normal path when everything works, but also what happens when nodes fail, replicas become inconsistent, or the network separates parts of the system.

The key idea is that a distributed database is not simply a larger database. It is a distributed system, and therefore its architecture must explicitly deal with partitioning, replication, failures, network communication, consistency, and availability.