<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Data-Modeling on IT Comparison</title><link>https://comparison.metacog.co.kr/tags/data-modeling/</link><description>Recent content in Data-Modeling on IT Comparison</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 06 Sep 2026 09:53:08 +0900</lastBuildDate><atom:link href="https://comparison.metacog.co.kr/tags/data-modeling/index.xml" rel="self" type="application/rss+xml"/><item><title>Single Model vs CQRS: One Data Model vs Split Read/Write Models</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-single-model-vs-cqrs-one-data-model-vs-split-read-write-mode/</link><pubDate>Sun, 06 Sep 2026 09:53:08 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-single-model-vs-cqrs-one-data-model-vs-split-read-write-mode/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;A &lt;strong class="kw"&gt;single model&lt;/strong&gt; uses one unified set of classes and one schema for both reading and writing data, while &lt;strong class="kw"&gt;CQRS&lt;/strong&gt; (Command Query Responsibility Segregation) splits the system into separate write models (commands) and read models (queries) that can use different schemas, storage, or even databases. The choice matters because it trades simplicity and consistency for scalability and query flexibility as read and write demands diverge.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="160" y="32" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Single Model&lt;/text&gt;&lt;text x="480" y="32" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;CQRS&lt;/text&gt;&lt;rect x="60" y="60" width="90" height="40" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="105" y="85" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Read Req&lt;/text&gt;&lt;rect x="230" y="60" width="90" height="40" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="275" y="85" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Write Req&lt;/text&gt;&lt;path d="M150,80 L200,80" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA1)"/&gt;&lt;path d="M230,80 L200,80" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA2)"/&gt;&lt;rect x="140" y="140" width="140" height="60" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="2"/&gt;&lt;text x="210" y="165" text-anchor="middle" font-size="13" style="fill:var(--primary)"&gt;Model&lt;/text&gt;&lt;text x="210" y="183" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;reads + writes&lt;/text&gt;&lt;path d="M200,100 L210,140" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="140" y="250" width="140" height="50" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="210" y="280" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;One DB / Schema&lt;/text&gt;&lt;path d="M210,200 L210,250" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA3)"/&gt;&lt;rect x="390" y="60" width="90" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="435" y="85" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Query&lt;/text&gt;&lt;rect x="520" y="60" width="90" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="565" y="85" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Command&lt;/text&gt;&lt;rect x="395" y="140" width="90" height="55" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;text x="440" y="163" text-anchor="middle" font-size="12" style="fill:var(--primary)"&gt;Read Model&lt;/text&gt;&lt;text x="440" y="180" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;optimized view&lt;/text&gt;&lt;rect x="525" y="140" width="90" height="55" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;text x="570" y="163" text-anchor="middle" font-size="12" style="fill:var(--primary)"&gt;Write Model&lt;/text&gt;&lt;text x="570" y="180" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;domain logic&lt;/text&gt;&lt;path d="M435,100 L440,140" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;path d="M565,100 L570,140" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="395" y="260" width="90" height="45" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="440" y="287" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Read Store&lt;/text&gt;&lt;rect x="525" y="260" width="90" height="45" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="570" y="287" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Write Store&lt;/text&gt;&lt;path d="M440,195 L440,260" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB1)"/&gt;&lt;path d="M570,195 L570,260" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB2)"/&gt;&lt;path d="M525,282 C505,320 465,320 445,282" fill="none" style="stroke:var(--secondary)" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrowB3)"/&gt;&lt;text x="485" y="335" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;sync / events&lt;/text&gt;&lt;defs&gt;&lt;marker id="arrowA1" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowA2" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowA3" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB1" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB2" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB3" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--secondary)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Single Model&lt;/th&gt;
&lt;th&gt;CQRS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request entry point&lt;/td&gt;
&lt;td&gt;One API/service handles reads and writes through the same code path&lt;/td&gt;
&lt;td&gt;Requests are split upfront into command handlers and query handlers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data model shape&lt;/td&gt;
&lt;td&gt;One set of classes/entities represents the domain for every operation&lt;/td&gt;
&lt;td&gt;Separate write model (rich domain logic) and read model (denormalized, query-optimized)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write path&lt;/td&gt;
&lt;td&gt;Write validates and persists directly to the shared schema&lt;/td&gt;
&lt;td&gt;Command handler validates, applies business rules, persists to the write store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read path&lt;/td&gt;
&lt;td&gt;Read queries the same schema writes use, often requiring joins&lt;/td&gt;
&lt;td&gt;Query handler reads from a precomputed, often denormalized read store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync between models&lt;/td&gt;
&lt;td&gt;Not applicable — there is only one model, so no sync is needed&lt;/td&gt;
&lt;td&gt;Read store is updated via events or projections after each write, introducing lag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency guarantee&lt;/td&gt;
&lt;td&gt;Strong consistency by default since reads see writes immediately&lt;/td&gt;
&lt;td&gt;Eventual consistency between write and read sides unless engineered otherwise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling behavior&lt;/td&gt;
&lt;td&gt;Read and write load scale together since they share infrastructure&lt;/td&gt;
&lt;td&gt;Read and write sides scale independently to match different load profiles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational complexity&lt;/td&gt;
&lt;td&gt;Low — one schema, one deployment, one mental model to maintain&lt;/td&gt;
&lt;td&gt;Higher — multiple stores, projection/event pipelines, and eventual-consistency debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Single model keeps one schema for everything; CQRS splits into a &lt;strong class="kw"&gt;write model&lt;/strong&gt; and a &lt;strong class="kw"&gt;read model&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;CQRS trades immediate consistency for &lt;strong class="kw"&gt;eventual consistency&lt;/strong&gt; via projections or events&lt;/li&gt;
&lt;li&gt;Single model is simpler to reason about; CQRS adds &lt;strong class="kw"&gt;operational overhead&lt;/strong&gt; from syncing multiple stores&lt;/li&gt;
&lt;li&gt;CQRS enables independent &lt;strong class="kw"&gt;scaling&lt;/strong&gt; of reads and writes; single model scales them together&lt;/li&gt;
&lt;li&gt;Query flexibility is higher in CQRS since read models can be shaped per &lt;strong class="kw"&gt;use case&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Single Model&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>SQL vs NoSQL: Relational Tables vs Flexible Data Models</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-sql-vs-nosql-relational-tables-vs-flexible-data-models/</link><pubDate>Sun, 06 Sep 2026 09:37:35 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-sql-vs-nosql-relational-tables-vs-flexible-data-models/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;SQL and NoSQL databases differ in how they structure, store, and query data: SQL enforces a &lt;strong class="kw"&gt;fixed schema&lt;/strong&gt; of related tables joined by keys, while NoSQL favors a &lt;strong class="kw"&gt;flexible schema&lt;/strong&gt; optimized for scale and varied data shapes. The choice affects everything from how you model relationships to how the system behaves under heavy write load or schema change.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="150" y="36" text-anchor="middle" font-size="20" style="fill:var(--primary)"&gt;SQL&lt;/text&gt;&lt;text x="490" y="36" text-anchor="middle" font-size="20" style="fill:var(--primary)"&gt;NoSQL&lt;/text&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4 4"/&gt;&lt;rect x="40" y="70" width="160" height="90" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="40" y1="100" x2="200" y2="100" style="stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;line x1="120" y1="70" x2="120" y2="160" style="stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;text x="48" y="90" font-size="12" style="fill:var(--primary)"&gt;Users&lt;/text&gt;&lt;text x="48" y="118" font-size="11" style="fill:var(--content)"&gt;id&lt;/text&gt;&lt;text x="128" y="118" font-size="11" style="fill:var(--content)"&gt;name&lt;/text&gt;&lt;text x="48" y="140" font-size="11" style="fill:var(--content)"&gt;1&lt;/text&gt;&lt;text x="128" y="140" font-size="11" style="fill:var(--content)"&gt;Alice&lt;/text&gt;&lt;rect x="40" y="200" width="160" height="110" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="40" y1="230" x2="200" y2="230" style="stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;line x1="100" y1="200" x2="100" y2="310" style="stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;line x1="150" y1="200" x2="150" y2="310" style="stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;text x="48" y="220" font-size="12" style="fill:var(--primary)"&gt;Orders&lt;/text&gt;&lt;text x="48" y="248" font-size="10" style="fill:var(--content)"&gt;id&lt;/text&gt;&lt;text x="106" y="248" font-size="10" style="fill:var(--content)"&gt;user_id&lt;/text&gt;&lt;text x="156" y="248" font-size="10" style="fill:var(--content)"&gt;item&lt;/text&gt;&lt;text x="48" y="270" font-size="10" style="fill:var(--content)"&gt;9&lt;/text&gt;&lt;text x="106" y="270" font-size="10" style="fill:var(--content)"&gt;1&lt;/text&gt;&lt;text x="156" y="270" font-size="10" style="fill:var(--content)"&gt;Book&lt;/text&gt;&lt;path d="M 106 200 Q 106 165 118 160" fill="none" style="stroke:var(--compare-a)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;&lt;text x="210" y="180" font-size="10" style="fill:var(--secondary)"&gt;foreign key join&lt;/text&gt;&lt;rect x="380" y="70" width="220" height="240" rx="10" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="396" y="96" font-size="12" style="fill:var(--primary)"&gt;{&lt;/text&gt;&lt;text x="412" y="118" font-size="11" style="fill:var(--content)"&gt;"id": 1,&lt;/text&gt;&lt;text x="412" y="140" font-size="11" style="fill:var(--content)"&gt;"name": "Alice",&lt;/text&gt;&lt;text x="412" y="162" font-size="11" style="fill:var(--content)"&gt;"orders": [&lt;/text&gt;&lt;text x="432" y="184" font-size="11" style="fill:var(--content)"&gt;{ "item": "Book" },&lt;/text&gt;&lt;text x="432" y="206" font-size="11" style="fill:var(--content)"&gt;{ "item": "Pen" }&lt;/text&gt;&lt;text x="412" y="228" font-size="11" style="fill:var(--content)"&gt;]&lt;/text&gt;&lt;text x="396" y="250" font-size="12" style="fill:var(--primary)"&gt;}&lt;/text&gt;&lt;text x="490" y="290" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;embedded, self-contained document&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;SQL&lt;/th&gt;
&lt;th&gt;NoSQL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data model&lt;/td&gt;
&lt;td&gt;Rows in normalized tables with fixed columns&lt;/td&gt;
&lt;td&gt;Documents, key-value pairs, wide columns, or graphs with flexible fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema definition&lt;/td&gt;
&lt;td&gt;Defined upfront; changes require migrations (ALTER TABLE)&lt;/td&gt;
&lt;td&gt;Schema-on-read; fields can vary per record without migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relationships&lt;/td&gt;
&lt;td&gt;Modeled explicitly via foreign keys and JOINs&lt;/td&gt;
&lt;td&gt;Modeled by embedding related data or denormalizing across documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query language&lt;/td&gt;
&lt;td&gt;Standardized SQL across most vendors&lt;/td&gt;
&lt;td&gt;Vendor-specific APIs or query languages (e.g. MongoDB query, CQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transactions &amp;amp; consistency&lt;/td&gt;
&lt;td&gt;ACID guarantees across multi-row/multi-table operations&lt;/td&gt;
&lt;td&gt;Often eventual consistency; ACID typically limited to single-document scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling approach&lt;/td&gt;
&lt;td&gt;Primarily vertical scaling; sharding is possible but complex&lt;/td&gt;
&lt;td&gt;Built for horizontal scaling via native partitioning/sharding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best-fit workload&lt;/td&gt;
&lt;td&gt;Structured data with complex, ad-hoc relational queries&lt;/td&gt;
&lt;td&gt;High-volume, high-velocity data with evolving or hierarchical structure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SQL requires a &lt;strong class="kw"&gt;fixed schema&lt;/strong&gt; agreed on before writing data; NoSQL allows each record to carry its own shape&lt;/li&gt;
&lt;li&gt;Relational databases resolve relationships through &lt;strong class="kw"&gt;JOINs&lt;/strong&gt;, while NoSQL typically resolves them through &lt;strong class="kw"&gt;embedding&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;SQL guarantees &lt;strong class="kw"&gt;ACID transactions&lt;/strong&gt; across tables; most NoSQL systems trade that for &lt;strong class="kw"&gt;eventual consistency&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;SQL systems scale primarily by &lt;strong class="kw"&gt;scaling up&lt;/strong&gt; hardware; NoSQL systems are designed to &lt;strong class="kw"&gt;scale out&lt;/strong&gt; across nodes&lt;/li&gt;
&lt;li&gt;Query language is a &lt;strong class="kw"&gt;standardized&lt;/strong&gt; across SQL vendors, whereas NoSQL query APIs are largely &lt;strong class="kw"&gt;proprietary&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;SQL&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Event Sourcing vs State-Based Persistence: Storing Changes vs Storing Current State</title><link>https://comparison.metacog.co.kr/posts/2026-08-04-event-sourcing-vs-state-based-persistence-storing-changes-vs/</link><pubDate>Tue, 04 Aug 2026 05:10:16 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-04-event-sourcing-vs-state-based-persistence-storing-changes-vs/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Event sourcing persists every change to an entity as an immutable, ordered &lt;strong class="kw"&gt;event log&lt;/strong&gt;, deriving current state by replaying that history. State-based persistence instead stores and overwrites a single &lt;strong class="kw"&gt;current record&lt;/strong&gt;, discarding prior values on each update. The choice affects auditability, storage growth, and how much replay/projection machinery you need to build.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;text x="160" y="32" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Event Sourcing&lt;/text&gt;&lt;rect x="60" y="52" width="200" height="32" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="72" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;AccountOpened&lt;/text&gt;&lt;rect x="60" y="90" width="200" height="32" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="110" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Deposited $100&lt;/text&gt;&lt;rect x="60" y="128" width="200" height="32" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="148" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Withdrew $30&lt;/text&gt;&lt;rect x="60" y="166" width="200" height="32" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="186" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Deposited $50&lt;/text&gt;&lt;line x1="160" y1="198" x2="160" y2="238" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="178" y="222" style="fill:var(--secondary)" font-size="10"&gt;replay&lt;/text&gt;&lt;rect x="85" y="246" width="150" height="34" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="2"/&gt;&lt;text x="160" y="268" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;Balance: $120&lt;/text&gt;&lt;text x="160" y="304" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Full history preserved&lt;/text&gt;&lt;text x="480" y="32" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;State-Based Persistence&lt;/text&gt;&lt;rect x="400" y="56" width="160" height="32" rx="3" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3,3"/&gt;&lt;text x="480" y="76" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Balance: $0&lt;/text&gt;&lt;line x1="480" y1="88" x2="480" y2="104" style="stroke:var(--border)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;rect x="400" y="106" width="160" height="32" rx="3" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3,3"/&gt;&lt;text x="480" y="126" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Balance: $100&lt;/text&gt;&lt;line x1="480" y1="138" x2="480" y2="154" style="stroke:var(--border)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;rect x="400" y="156" width="160" height="32" rx="3" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3,3"/&gt;&lt;text x="480" y="176" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Balance: $70&lt;/text&gt;&lt;line x1="480" y1="188" x2="480" y2="210" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="500" y="202" style="fill:var(--secondary)" font-size="10"&gt;overwrite&lt;/text&gt;&lt;rect x="405" y="216" width="150" height="34" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;text x="480" y="238" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;Balance: $120&lt;/text&gt;&lt;text x="480" y="304" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Only latest state kept&lt;/text&gt;&lt;defs&gt;&lt;marker id="arrowA" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--border)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Event Sourcing&lt;/th&gt;
&lt;th&gt;State-Based Persistence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Write operation&lt;/td&gt;
&lt;td&gt;Appends a new immutable event to the log; nothing is ever modified in place&lt;/td&gt;
&lt;td&gt;Updates or overwrites the existing row/record with new values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data representation&lt;/td&gt;
&lt;td&gt;Sequence of domain events describing what happened&lt;/td&gt;
&lt;td&gt;Single current snapshot of the entity&amp;rsquo;s fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reading current state&lt;/td&gt;
&lt;td&gt;Replay events from the start (or from a snapshot) to derive current state&lt;/td&gt;
&lt;td&gt;Direct read of the stored row, no reconstruction needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency conflicts&lt;/td&gt;
&lt;td&gt;Detected by checking the expected event stream version before appending&lt;/td&gt;
&lt;td&gt;Detected via optimistic locking (row version/timestamp) or DB-level locks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historical/audit trail&lt;/td&gt;
&lt;td&gt;Native and complete — every past state is reconstructable&lt;/td&gt;
&lt;td&gt;Not retained by default; requires a separate audit log or triggers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema evolution&lt;/td&gt;
&lt;td&gt;Old event versions must be upcast or handled explicitly by consumers&lt;/td&gt;
&lt;td&gt;Existing rows are migrated or altered directly to the new shape&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage growth&lt;/td&gt;
&lt;td&gt;Grows unbounded with every change; needs snapshotting/compaction to stay fast&lt;/td&gt;
&lt;td&gt;Stays roughly proportional to the number of entities, not their history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query complexity&lt;/td&gt;
&lt;td&gt;Ad-hoc queries need dedicated projections/read models built from the events&lt;/td&gt;
&lt;td&gt;Supports direct ad-hoc SQL queries against the current data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Event sourcing stores an append-only &lt;strong class="kw"&gt;event log&lt;/strong&gt;; state-based persistence stores only the &lt;strong class="kw"&gt;current row&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Rebuilding state requires &lt;strong class="kw"&gt;replay&lt;/strong&gt; in event sourcing, versus a simple read in state-based systems&lt;/li&gt;
&lt;li&gt;Event sourcing gives a native &lt;strong class="kw"&gt;audit trail&lt;/strong&gt;; state-based needs bolt-on logging to recover history&lt;/li&gt;
&lt;li&gt;Concurrency is resolved via &lt;strong class="kw"&gt;event versioning&lt;/strong&gt; instead of row-level locking&lt;/li&gt;
&lt;li&gt;Storage grows unbounded without &lt;strong class="kw"&gt;snapshots&lt;/strong&gt; in event sourcing, while state-based storage stays compact&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Event Sourcing&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>SQL vs NoSQL: Relational Tables vs Flexible Documents</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-sql-vs-nosql-relational-tables-vs-flexible-documents/</link><pubDate>Sun, 02 Aug 2026 07:42:50 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-sql-vs-nosql-relational-tables-vs-flexible-documents/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;SQL (relational) databases organize data into fixed-schema tables linked by foreign keys and queried with a standardized language, prioritizing consistency and structured relationships. NoSQL (non-relational) databases store data as documents, key-value pairs, wide columns, or graphs with flexible or absent schemas, prioritizing horizontal scale and adaptability. The right choice depends on how relational your data is and whether you need strict consistency or elastic scale.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="155" y="30" text-anchor="middle" font-size="16" font-weight="bold" style="fill:var(--primary)"&gt;SQL (Relational)&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="16" font-weight="bold" style="fill:var(--primary)"&gt;NoSQL (Non-Relational)&lt;/text&gt;&lt;line x1="320" y1="45" x2="320" y2="345" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;rect x="50" y="55" width="160" height="90" style="fill:none;stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="50" y="55" width="160" height="24" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="130" y="71" text-anchor="middle" font-size="12" font-weight="bold" style="fill:var(--primary)"&gt;users&lt;/text&gt;&lt;line x1="130" y1="79" x2="130" y2="145" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;text x="90" y="95" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;id&lt;/text&gt;&lt;text x="170" y="95" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;name&lt;/text&gt;&lt;text x="90" y="115" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;1&lt;/text&gt;&lt;text x="170" y="115" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Alice&lt;/text&gt;&lt;text x="90" y="135" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;2&lt;/text&gt;&lt;text x="170" y="135" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Bob&lt;/text&gt;&lt;rect x="50" y="180" width="210" height="90" style="fill:none;stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="50" y="180" width="210" height="24" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="155" y="196" text-anchor="middle" font-size="12" font-weight="bold" style="fill:var(--primary)"&gt;orders&lt;/text&gt;&lt;line x1="120" y1="204" x2="120" y2="270" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;line x1="190" y1="204" x2="190" y2="270" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;text x="85" y="220" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;id&lt;/text&gt;&lt;text x="155" y="220" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;user_id&lt;/text&gt;&lt;text x="225" y="220" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;item&lt;/text&gt;&lt;text x="85" y="240" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;101&lt;/text&gt;&lt;text x="155" y="240" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;1&lt;/text&gt;&lt;text x="225" y="240" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Book&lt;/text&gt;&lt;text x="85" y="258" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;102&lt;/text&gt;&lt;text x="155" y="258" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;1&lt;/text&gt;&lt;text x="225" y="258" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Pen&lt;/text&gt;&lt;path d="M155,204 C155,170 130,170 130,148" style="fill:none;stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="130,148 126,156 134,156" style="fill:var(--compare-a)"/&gt;&lt;text x="155" y="300" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Data split across tables,&lt;/text&gt;&lt;text x="155" y="314" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;joined via foreign keys&lt;/text&gt;&lt;rect x="380" y="55" width="210" height="250" rx="8" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="485" y="76" text-anchor="middle" font-size="12" font-weight="bold" style="fill:var(--primary)"&gt;user document&lt;/text&gt;&lt;text x="395" y="100" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;{&lt;/text&gt;&lt;text x="405" y="118" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;"id": 1,&lt;/text&gt;&lt;text x="405" y="136" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;"name": "Alice",&lt;/text&gt;&lt;text x="405" y="154" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;"orders": [&lt;/text&gt;&lt;text x="415" y="172" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;{ "id": 101,&lt;/text&gt;&lt;text x="425" y="188" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;"item": "Book" },&lt;/text&gt;&lt;text x="415" y="206" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;{ "id": 102,&lt;/text&gt;&lt;text x="425" y="222" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;"item": "Pen" }&lt;/text&gt;&lt;text x="405" y="240" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;]&lt;/text&gt;&lt;text x="395" y="258" font-family="monospace" font-size="11" style="fill:var(--content)"&gt;}&lt;/text&gt;&lt;text x="485" y="300" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Related data embedded&lt;/text&gt;&lt;text x="485" y="314" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;in one flexible document&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;SQL (Relational)&lt;/th&gt;
&lt;th&gt;NoSQL (Non-Relational)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data model&lt;/td&gt;
&lt;td&gt;Tables with fixed rows/columns, normalized via foreign keys&lt;/td&gt;
&lt;td&gt;Documents, key-value pairs, wide-column, or graph structures with per-record flexibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema&lt;/td&gt;
&lt;td&gt;Schema-on-write, enforced by the engine (types, constraints, CREATE TABLE)&lt;/td&gt;
&lt;td&gt;Schema-on-read; little to no enforcement, validation left to the application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query language&lt;/td&gt;
&lt;td&gt;Standardized SQL (SELECT, JOIN, WHERE)&lt;/td&gt;
&lt;td&gt;Varies by product — Mongo query API, CQL, Gremlin, or simple key lookups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency model&lt;/td&gt;
&lt;td&gt;Strong ACID transactions across tables by default&lt;/td&gt;
&lt;td&gt;Often eventual/tunable consistency (BASE); some now offer document-level ACID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling approach&lt;/td&gt;
&lt;td&gt;Vertical scaling first; horizontal sharding possible but complex&lt;/td&gt;
&lt;td&gt;Built for horizontal scaling/sharding across commodity nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relationships&lt;/td&gt;
&lt;td&gt;Modeled via joins and foreign keys&lt;/td&gt;
&lt;td&gt;Modeled via embedding (denormalization) or manual reference resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical examples&lt;/td&gt;
&lt;td&gt;PostgreSQL, MySQL, SQL Server, Oracle&lt;/td&gt;
&lt;td&gt;MongoDB, Cassandra, DynamoDB, Redis, Neo4j&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit workload&lt;/td&gt;
&lt;td&gt;Complex multi-entity queries, reporting, transactional integrity&lt;/td&gt;
&lt;td&gt;High-volume writes, evolving schemas, massive horizontal scale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SQL normalizes data into related tables with a fixed schema enforced at write time; NoSQL stores flexible, often denormalized records with schema left to the application.&lt;/li&gt;
&lt;li&gt;SQL guarantees ACID transactions across tables by default; most NoSQL stores trade strict consistency for availability/partition tolerance (BASE).&lt;/li&gt;
&lt;li&gt;SQL relationships require JOINs across tables; NoSQL typically embeds related data in one document to avoid joins, or resolves references manually.&lt;/li&gt;
&lt;li&gt;SQL databases scale vertically first and shard with effort; NoSQL databases are architected from the start for horizontal, distributed scaling.&lt;/li&gt;
&lt;li&gt;Changing a SQL schema requires a migration; NoSQL documents can differ in shape from record to record with no migration needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;SQL (Relational)&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>