Theoretical Foundations
Welcome to the curriculum workspace. Here you will find long-form technical guidelines outlining core architectural blueprints and implementation mechanics.
Module 7: Distributed Computing Realities & The CAP Theorem
PREREQUISITE STATEMENT: Read this module after completing Module 6 (Caching Topologies). Understanding the physics of latency and cache invalidation prepares you to reason about the mathematical impossibility of maintaining instantaneous consistency across physically separate locations when communication link failures occur.
1. Introduction: The Realities of Distributed Systems
In single-node software architecture, we take reliability, order, and synchronization for granted. Inside a single server's chassis, calling a class constructor or retrieving a database record from RAM behaves predictably. Memory reads are atomic, thread schedulers maintain a single execution timeline, and failures are typically binary (the server is either alive or dead).
When we scale out to a distributed system, this predictable reality collapses. Computers communicate over network wires, and networks are inherently asynchronous, untrusted, and volatile. If you send a command from Node A to Node B, three potential states exist for every transmission:
- The request succeeded (received and processed by Node B).
- The request failed (rejected by Node B or blocked by the network).
- The request is in an indeterminate state (Node B received and processed it, but the response packet was dropped before returning to Node A; or the request packet is stuck in a router queue, and Node B has not received it yet).
In a distributed environment, you cannot distinguish between a slow network route, a crashed remote processor, or a dropped packet without waiting. This fundamental uncertainty is the core challenge of distributed systems engineering.
2. The 8 Fallacies of Distributed Computing
In the early 1990s, Peter Deutsch and other systems pioneers at Sun Microsystems compiled a list of common false assumptions that engineers make when transitioning from single-node software development to distributed systems design. These are known as the 8 Fallacies of Distributed Computing.
graph TD
Root["π The 8 Fallacies of\nDistributed Computing"]
Root --> Cat1["π‘ Network Infrastructure"]
Root --> Cat2["πΊοΈ Topology & Control"]
Root --> Cat3["πΈ Economics & Environment"]
Cat1 --> F1["1. The Network\nis Reliable"]
Cat1 --> F2["2. Latency\nis Zero"]
Cat1 --> F3["3. Bandwidth\nis Infinite"]
Cat1 --> F4["4. The Network\nis Secure"]
Cat2 --> F5["5. Topology\nDoesn't Change"]
Cat2 --> F6["6. There is One\nAdministrator"]
Cat3 --> F7["7. Transport\nCost is Zero"]
Cat3 --> F8["8. The Network\nis Homogeneous"]
classDef root fill:#1a1f35,stroke:#d4a359,stroke-width:2px,color:#d4a359,font-weight:bold
classDef category fill:#0f1420,stroke:#4a5568,stroke-width:1px,color:#9ca3af,font-weight:bold
classDef fallacy fill:#121623,stroke:#374151,stroke-width:1px,color:#e5e7eb
class Root root
class Cat1,Cat2,Cat3 category
class F1,F2,F3,F4,F5,F6,F7,F8 fallacy
Fallacy 1: The Network is Reliable
- The Assumption: A packet sent will always arrive at its destination.
- The Reality: Fiber lines are severed, switches lose power, TCP buffers overflow, and packet collision causes silent drops. At scale, hardware failures are a daily occurrence.
- Mitigation: Software must implement timeouts, retry loops with backoff, idempotent operations, and state reconciliation processes.
Fallacy 2: Latency is Zero
- The Assumption: Fetching data over the network is as fast as reading it from local memory or local disk.
- The Reality: Network packet transmission is bounded by the speed of light in fiber optic cables (approximately $200\text{ km/ms}$). While local RAM references take $\approx 100\text{ ns}$, a round trip between Virginia and Tokyo takes $\approx 150\text{ ms}$ (a difference of six orders of magnitude).
- Mitigation: Implement aggressive caching, regional CDNs, data locality strategies, and batching of network requests to minimize round trips.
Fallacy 3: Bandwidth is Infinite
- The Assumption: You can transfer arbitrary amounts of data between services without consequence.
- The Reality: Network cards, switch backplanes, and regional fiber routes have physical limits (e.g., 10Gbps interfaces). Senders can easily saturate network pipes, causing queue congestion, packet drop, and latency spikes.
- Mitigation: Use compact binary serialization formats (Protobuf, Avro) instead of verbose XML/JSON, implement response compression (Gzip, Brotli), paginate API responses, and avoid fetching unnecessary database columns.
Fallacy 4: The Network is Secure
- The Assumption: Only authorized entities can see or modify packets inside your corporate network.
- The Reality: Inside modern cloud VPCs, data transit is vulnerable to packet sniffing, man-in-the-middle attacks, and internal security compromises.
- Mitigation: Encrypt all traffic in transit using Mutual TLS (mTLS), enforce strict service-to-service authorization (using IAM policies or Service Meshes), and sign messages using cryptographic tokens.
Fallacy 5: Topology Doesn't Change
- The Assumption: IP addresses, servers, and routes remain fixed over the lifetime of the application.
- The Reality: Cloud environments dynamically scale nodes in and out, containers restart on different hosts with different IPs, and virtual routers shift packet paths dynamically.
- Mitigation: Implement dynamic Service Discovery patterns, use load balancers with health checks, and avoid hardcoding IP addresses in configuration files.
Fallacy 6: There is One Administrator
- The Assumption: A single engineering team controls and understands the entire network and system configuration.
- The Reality: Modern systems rely on third-party SaaS integrations, cloud provider infrastructure, and multiple independent engineering teams managing their own VPCs.
- Mitigation: Establish strict, versioned API contracts, implement circuit breakers for external integrations, and design systems to degrade gracefully when external networks are unavailable.
Fallacy 7: Transport Cost is Zero
- The Assumption: Moving data across network boundaries carries no financial cost.
- The Reality: Cloud providers charge egress fees for transferring data across Availability Zones ($0.01/GB$) and across geographic regions ($0.02/GB$). High-throughput systems can generate network bills exceeding their compute bills.
- Mitigation: Design architectures that favor data locality (processing data in the same AZ or region), cache responses at the edge, and use lightweight telemetry systems.
Fallacy 8: The Network is Homogeneous
- The Assumption: All network switches, servers, protocols, and operating systems in the system are identical.
- The Reality: A distributed system contains a mix of Linux servers, mobile client browsers, legacy Windows VMs, differing network interface configurations, and varying MTU (Maximum Transmission Unit) sizes.
- Mitigation: Standardize on open protocols (HTTP/2, gRPC), validate serialization boundaries, and perform multi-platform compatibility testing.
3. The CAP Theorem
Formulated by Eric Brewer in 2000 and mathematically proven by Seth Gilbert and Nancy Lynch in 2002, the CAP Theorem defines the fundamental boundary of distributed database design.
[ The CAP Triangle ]
Consistency (CP)
/ \
/ \
/ \
/ P \
/_________\
Availability (AP) Partition Tolerance (CA - Impossible)
The theorem states that a distributed data store can guarantee at most two of the following three properties simultaneously:
- Consistency (C): Every read receives the most recent write or an error. The system behaves as if there is only a single copy of the data (also known as Atomic Consistency or Linearizability).
- Availability (A): Every non-failing node returns a non-error response for every request it receives, without guaranteeing that it contains the most recent write. (You cannot return an error or block the client).
- Partition Tolerance (P): The system continues to operate despite an arbitrary number of messages being dropped or delayed by the network between nodes.
A. The Partition Reality: P is Not Optional
In a physical network, hardware, switches, and fiber links will eventually drop packets or split communication routes. This state is called a Network Partition. Because you cannot build a physical network that never drops a packet, Partition Tolerance (P) is not optional. You must assume your network will partition.
Therefore, the practical choice is not "C vs. A vs. P". The choice is: In the event of a network partition (P), does the system choose Consistency (CP) or Availability (AP)?
[ System During Network Partition ]
|
+-------------------+-------------------+
| |
[ CP: Consistency ] [ AP: Availability ]
- Rejects writes/reads if out-of-sync - Accepts reads & writes on all nodes
- Returns Error / Blocks client - Returns stale data, resolves later
- Focuses on correctness - Focuses on system uptime
B. Gilbert & Lynch Impossibility Proof Scenario
Let's model the mathematical impossibility of achieving both atomic consistency and 100% availability during a network partition.
Suppose we have a two-node distributed database cluster: Node A and Node B. A network partition occurs, cutting the connection between the nodes. The nodes can no longer communicate.
[Client 1] [Client 2]
| |
v v
[ Node A ] ==========( Network Split )========== [ Node B ]
- Step 1: Client 1 sends a write request to Node A:
set value = "v1". - Step 2: Because of the network partition, Node A cannot propagate this write to Node B.
- Step 3: Client 2 sends a read request to Node B:
get value.
The Architectural Dilemma:
- If Node B chooses Consistency (CP): Node B knows it has lost contact with Node A and cannot verify if its local value is up to date. Node B must block the read or return an error response (e.g.
HTTP 500 Internal Server Error). The system is Consistent but Unavailable. - If Node B chooses Availability (AP): Node B responds immediately with its local value (
"v0"). The client receives a successful response, but the data is stale. The system is Available but Inconsistent.
This scenario proves that no software algorithm can bypass the physical constraint: you must choose between returning correct data (CP) or returning a successful response (AP) when communication links break.
4. The PACELC Theorem
In 2012, Daniel Abadi published the PACELC Theorem, noting that the CAP Theorem only focuses on system behavior during a network partition. In reality, partitions are rare. Systems spend 99.9% of their time operating under normal network conditions.
PACELC extends CAP by mapping the trade-offs during both partitioned and normal operations:
$$\text{If there is a } \mathbf{P} \text{ (Partition), trade off } \mathbf{A} \text{ (Availability) or } \mathbf{C} \text{ (Consistency);}$$ $$\text{Else } (\mathbf{E}), \text{ during normal execution, trade off } \mathbf{L} \text{ (Latency) or } \mathbf{C} \text{ (Consistency).}$$
[ PACELC Matrix ]
Normal Condition
/ \
Low Latency (L) Consistency (C)
/ \ / \
AP (Cassandra) [PA/EL] [PA/EC] [PC/EL] (MongoDB) [PC/EC] (Spanner)
\ / \ /
Network Partition
A. PACELC System Classifications
Under PACELC, distributed databases are classified into four primary architectures:
- PC/EC (Consistent / Consistent):
- Partition: Choose Consistency (C).
- Normal: Choose Consistency (C).
- Behavior: During partitions, writes/reads block to prevent split-brain. Under normal conditions, writes require synchronous replication across replicas before completing, adding round-trip latency to guarantee linearizability.
- Example: Google Spanner, CockroachDB.
- PA/EL (Available / Latency):
- Partition: Choose Availability (A).
- Normal: Choose Latency (L).
- Behavior: During partitions, replicas accept writes independently. Under normal conditions, writes are returned immediately after writing locally, and updates propagate asynchronously, minimizing latency.
- Example: Apache Cassandra, AWS DynamoDB.
- PC/EL (Consistent / Latency):
- Partition: Choose Consistency (C).
- Normal: Choose Latency (L).
- Behavior: During partitions, the system shuts down lagging replica writes. Under normal conditions, the primary node handles writes, but reads can be served from local replica memory to achieve low latency at the cost of potential stale reads.
- Example: MongoDB (with primary-only writes and secondary reads).
- PA/EC (Available / Consistent):
- Partition: Choose Availability (A).
- Normal: Choose Consistency (C).
- Behavior: During partitions, nodes remain writable. Under normal conditions, the system executes heavy coordination protocols to maintain strong transactional consistency. (Extremely rare in practice).
B. PACELC Database Engine Mapping
| Database Engine | PACELC Classification | Partition Behavior | Normal Behavior | Architectural Rationale |
|---|---|---|---|---|
| Google Spanner | PC/EC | Blocks updates to isolated partition groups. | Sync replication via Paxos quorums and TrueTime. | Financial ledger accuracy; guarantees external consistency globally at the cost of higher write latency. |
| Apache Cassandra | PA/EL | Accepts local writes on partitioned nodes. | Async read/write replication based on client quorum configuration. | High-throughput write performance; design prioritizes local edge writes over real-time global consistency. |
| MongoDB | PC/EL | Primary steps down if it loses contact with the majority. | Default writes go to the primary node; reads can query secondary nodes. | Document store prioritizing single-document atomicity; read scaling trades off consistency for query speed. |
| AWS DynamoDB | PA/EL | Global Tables accept writes in any partitioned region. | Asynchronous replication between regions. | Amazon checkout shopping cart model: it is better to accept an order and resolve duplicates later than to reject a sale. |
5. Consensus Algorithms: Raft and Paxos Mechanics
To achieve CP (Consistency during Partitions), distributed databases rely on Consensus Algorithms. Consensus ensures that a cluster of machines can agree on a sequence of state transitions even if some nodes fail or packets are delayed.
A. The Raft Consensus Lifecycle
Raft decomposes consensus into three explicit subproblems:
- Leader Election: A leader is elected when an existing leader fails.
- Log Replication: The leader accepts log entries from clients and replicates them across followers.
- Safety: If any server has applied a log entry to its state machine, no other server can apply a different command for the same log index.
sequenceDiagram
participant Client
participant Leader as Leader (Node 1)
participant Follower1 as Follower (Node 2)
participant Follower2 as Follower (Node 3)
Client->>Leader: 1. Command ("set balance = 100")
Note over Leader: Append to Local Log (Uncommitted)
par Replicate Log Entries
Leader->>Follower1: 2. AppendEntries RPC
Leader->>Follower2: 2. AppendEntries RPC
end
Follower1-->>Leader: 3. Ack Success
Follower2-->>Leader: 3. Ack Success
Note over Leader: Quorum Achieved (2/3 Nodes)<br/>Commit Log Entry
Leader-->>Client: 4. Success Response
par Commit Notification
Leader->>Follower1: 5. Apply Commit
Leader->>Follower2: 5. Apply Commit
end
B. Mathematical Quorum Formulation
In a cluster of $N$ nodes, a quorum $Q$ is defined as any subset of nodes containing a strict majority: $$Q \ge \left\lfloor \frac{N}{2} \right\rfloor + 1$$
To tolerate $F$ node failures, the cluster must contain at least $2F + 1$ total nodes:
- A 3-node cluster can tolerate $F = 1$ failure ($Q = 2$).
- A 5-node cluster can tolerate $F = 2$ failures ($Q = 3$).
- An 8-node cluster provides no additional failure tolerance over a 7-node cluster ($F = 3$ for both), making odd-numbered cluster counts mandatory to optimize resource usage.
6. C# Production Implementations
Below are complete C# production implementations demonstrating the mechanics of both CP (Strong Quorum Consensus) and AP (Eventual Consistency with Vector Clocks).
A. CP Implementation: Quorum Consensus Replica Manager
This implementation enforces strict majority quorum writes and reads. If a network partition prevents achieving a majority, the write operation is aborted, guaranteeing linearizability.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MacroPatternsConsortium.DistributedSystems
{
public interface IDatabaseNode<TKey, TValue>
{
int NodeId { get; }
bool IsHealthy { get; set; }
Task<bool> WriteLocalAsync(TKey key, TValue value, long version, CancellationToken ct);
Task<(TValue Value, long Version, bool Success)> ReadLocalAsync(TKey key, CancellationToken ct);
}
public class DatabaseNode<TKey, TValue> : IDatabaseNode<TKey, TValue>
{
private readonly Dictionary<TKey, (TValue Value, long Version)> _store = new();
private readonly object _lock = new();
public int NodeId { get; }
public bool IsHealthy { get; set; } = true;
public DatabaseNode(int nodeId) => NodeId = nodeId;
public Task<bool> WriteLocalAsync(TKey key, TValue value, long version, CancellationToken ct)
{
if (!IsHealthy) return Task.FromResult(false);
lock (_lock)
{
if (_store.TryGetValue(key, out var current) && current.Version >= version)
{
return Task.FromResult(false); // Reject stale version
}
_store[key] = (value, version);
return Task.FromResult(true);
}
}
public Task<(TValue Value, long Version, bool Success)> ReadLocalAsync(TKey key, CancellationToken ct)
{
if (!IsHealthy) return Task.FromResult((default(TValue), 0, false));
lock (_lock)
{
if (_store.TryGetValue(key, out var record))
{
return Task.FromResult((record.Value, record.Version, true));
}
return Task.FromResult((default(TValue), 0, false));
}
}
}
public class QuorumConsensusCluster<TKey, TValue>
{
private readonly List<IDatabaseNode<TKey, TValue>> _nodes;
private readonly int _quorumSize;
private long _globalVersion;
public QuorumConsensusCluster(List<IDatabaseNode<TKey, TValue>> nodes)
{
_nodes = nodes ?? throw new ArgumentNullException(nameof(nodes));
_quorumSize = (nodes.Count / 2) + 1;
}
public async Task<bool> QuorumWriteAsync(TKey key, TValue value, CancellationToken ct = default)
{
long nextVersion = Interlocked.Increment(ref _globalVersion);
var writeTasks = _nodes.Select(node => node.WriteLocalAsync(key, value, nextVersion, ct)).ToList();
var results = await Task.WhenAll(writeTasks);
int successCount = results.Count(success => success);
if (successCount >= _quorumSize)
{
return true; // Quorum satisfied (CP Success)
}
// Failure: Abort operation to preserve linearizability across network partition
return false;
}
public async Task<(TValue Value, bool Success)> QuorumReadAsync(TKey key, CancellationToken ct = default)
{
var readTasks = _nodes.Select(node => node.ReadLocalAsync(key, ct)).ToList();
var results = await Task.WhenAll(readTasks);
var validResponses = results.Where(r => r.Success).ToList();
if (validResponses.Count < _quorumSize)
{
// Cannot achieve quorum read during partition
return (default(TValue), false);
}
// Return highest version value across quorum
var latestRecord = validResponses.OrderByDescending(r => r.Version).First();
return (latestRecord.Value, true);
}
}
}
B. AP Implementation: Asynchronous Replication with Vector Clock Reconciliation
This implementation prioritizes maximum availability. Replicas accept writes independently during network partitions, using Vector Clocks to resolve concurrent divergent updates when network partitions heal.
using System;
using System.Collections.Generic;
using System.Linq;
namespace MacroPatternsConsortium.DistributedSystems
{
public class VectorClock : IEquatable<VectorClock>
{
public Dictionary<int, long> Versions { get; } = new();
public VectorClock Increment(int nodeId)
{
var nextClock = new VectorClock();
foreach (var kvp in Versions) nextClock.Versions[kvp.Key] = kvp.Value;
if (!nextClock.Versions.ContainsKey(nodeId))
nextClock.Versions[nodeId] = 0;
nextClock.Versions[nodeId]++;
return nextClock;
}
public bool ConcurrentWith(VectorClock other)
{
return !Dominates(other) && !other.Dominates(this);
}
public bool Dominates(VectorClock other)
{
bool greaterInAtLeastOne = false;
foreach (var kvp in Versions)
{
long otherVer = other.Versions.TryGetValue(kvp.Key, out var v) ? v : 0;
if (kvp.Value < otherVer) return false;
if (kvp.Value > otherVer) greaterInAtLeastOne = true;
}
return greaterInAtLeastOne;
}
public bool Equals(VectorClock other)
{
if (other is null) return false;
if (Versions.Count != other.Versions.Count) return false;
return Versions.All(kvp => other.Versions.TryGetValue(kvp.Key, out var v) && v == kvp.Value);
}
}
public class APEventualStore<TKey, TValue>
{
private readonly int _nodeId;
private readonly Dictionary<TKey, List<(TValue Value, VectorClock Clock)>> _storage = new();
public APEventualStore(int nodeId) => _nodeId = nodeId;
public VectorClock Write(TKey key, TValue value, VectorClock clientClock = null)
{
clientClock ??= new VectorClock();
var newClock = clientClock.Increment(_nodeId);
if (!_storage.ContainsKey(key))
{
_storage[key] = new List<(TValue, VectorClock)>();
}
// Remove obsolete superseded versions
_storage[key].RemoveAll(item => newClock.Dominates(item.Clock));
_storage[key].Add((value, newClock));
return newClock;
}
public List<(TValue Value, VectorClock Clock)> Read(TKey key)
{
if (_storage.TryGetValue(key, out var entries))
{
return entries;
}
return new List<(TValue, VectorClock)>();
}
}
}
7. Diagnostics: Chaos Engineering & Network Degradation
To verify that your distributed system behaves correctly during partitions, you cannot rely on unit testing or code reviews. You must physically degrade the network in a staging environment to observe the failure states. This practice is known as Chaos Engineering.
A. Chaos Simulation Tools
- Linux
tc(Traffic Control): A kernel utility that manipulates the Linux network scheduler to inject latency, jitter, packet loss, and duplication. - Toxiproxy (by Shopify): A TCP proxy framework designed to simulate network conditions (latencies, bandwidth limits, TCP socket timeouts) directly in test suites.
- Chaos Mesh: A Kubernetes-native chaos engineering platform that orchestrates network partitions, pod crashes, and disk I/O bottlenecks.
B. Linux tc Command Recipes
Here are concrete shell commands to simulate network degradation on a Linux database host:
# 1. Inject a constant 100ms latency to all outgoing packets on interface eth0
sudo tc qdisc add dev eth0 root netem delay 100ms
# 2. Inject 100ms of latency with +/- 10ms of random jitter
sudo tc qdisc change dev eth0 root netem delay 100ms 10ms
# 3. Simulate 5% packet loss on the network interface
sudo tc qdisc change dev eth0 root netem loss 5%
# 4. Simulate a network partition: block all traffic to/from a specific IP range (e.g., 10.0.5.0/24)
sudo iptables -A OUTPUT -d 10.0.5.0/24 -j DROP
# 5. Clean up all network emulation rules
sudo tc qdisc del dev eth0 root
8. Documenting Failure: FMEA (Failure Mode and Effects Analysis)
An FMEA (Failure Mode and Effects Analysis) is a structured engineering document used to identify, analyze, and mitigate potential system failure modes. In distributed systems architecture, the FMEA helps quantify the impact of network splits and node failures.
The FMEA Scoring Matrix
For every failure mode, assign scores from 1 to 10 for:
- Severity (S): How catastrophic is the failure to the business? (1 = No impact, 10 = Total data loss/regulatory breach).
- Occurrence (O): How frequently does this failure happen? (1 = Almost never, 10 = Continuous).
- Detection (D): How difficult is it to detect the failure before it impacts the user? (1 = Instant alert, 10 = Invisible silent data corruption).
Calculate the Risk Priority Number (RPN): $$\text{RPN} = S \times O \times D$$ Mitigation efforts must prioritize failure modes with the highest RPN scores.
Distributed Systems FMEA Template
| System Component | Failure Mode | Severe Effect | S | O | D | RPN | Mitigation Strategy | MPC Module Link |
|---|---|---|---|---|---|---|---|---|
| Write Replication | Split-brain network partition between regional data centers. | Dual database nodes accept conflicting writes, leading to permanent data divergence. | 8 | 3 | 7 | 168 | Implement Paxos/Raft quorums; set database to reject writes unless majority quorum is achieved (CP). | Module 5: Storage |
| Downstream API Call | Downstream payment service experiences transient thread starvation. | Gateway thread pool exhausts waiting for socket responses; entire API collapses. | 9 | 4 | 2 | 72 | Implement Circuit Breaker pattern with a 2-second timeout and fallback payment queuing. | Module 14: Resiliency |
| Stateful Nodes | Primary database node experiences an ungraceful hardware crash. | Database goes offline; writes cannot be processed. | 9 | 3 | 2 | 54 | Deploy active-passive replication with automated sentinel failover and virtual IP routing. | Module 5: Storage |
| Cache Cluster | Centralized Redis cache cluster experiences memory eviction stampede. | Cache misses flood primary DB; DB CPU hits 100%, causing global connection timeouts. | 7 | 4 | 5 | 140 | Implement probabilistic cache early expiration (XFetch) and singleflight lock coordination on read misses. | Module 6: Caching |
9. Hands-on Architecture Challenge
Scenario Description
A network partition has occurred between Database Node A (in Virginia) and Database Node B (in Ireland). The nodes can no longer communicate (-.-). Client requests continue to arrive at both regions.
Your Goal:
- Model the CP (Consistent / Partition Tolerant) write routing behavior:
- Illustrate that the client sends a write request to Node A (the leader).
- Node A attempts to write to the majority quorum but cannot reach Node B.
- Illustrate that Node A rejects the client write with a
Write Error (Quorum Loss)to prevent data divergence.
- Model the AP (Available / Partition Tolerant) write routing behavior:
- Illustrate that client write requests are accepted on both Node A and Node B independently.
- Label that both return success responses immediately, resulting in a temporary
Divergent State(split-brain).
- Draw both scenarios side-by-side or sequentially in the architecture diagram.
10. Practice Challenge Template
Use this template in your sandbox to visualize the differences:
graph TD
subgraph CP_System [CP System - Quorum Loss Rejection]
Client1[Client] -->|1. Write Request| NodeA_CP[Node A - Leader]
NodeA_CP -.->|2. Replicate FAIL| NodeB_CP[Node B - Follower]
NodeA_CP -->|3. Return Write Error| Client1
style NodeA_CP fill:#f9f,stroke:#333,stroke-width:2px
style NodeB_CP fill:#9f9,stroke:#333,stroke-width:2px
end
subgraph AP_System [AP System - Accept & Diverge]
Client2[Client 1] -->|1. Write v1| NodeA_AP[Node A - Local Write]
Client3[Client 2] -->|1. Write v2| NodeB_AP[Node B - Local Write]
NodeA_AP -->|2. HTTP 200 OK| Client2
NodeB_AP -->|2. HTTP 200 OK| Client3
NodeA_AP -.->|Replication Partitioned| NodeB_AP
style NodeA_AP fill:#f9f,stroke:#333,stroke-width:2px
style NodeB_AP fill:#f9f,stroke:#333,stroke-width:2px
end
NEXT MODULE BRIDGE: Once you understand the trade-offs of CAP and PACELC, you will learn how to design application domain boundaries around these constraints. Proceed to Module 8: Monoliths to Domain-Driven Microservices to discover how to align Bounded Contexts with your data consistency needs.