| import { motion } from 'motion/react'; |
|
|
| export default function DenormalizationDiagram() { |
| return ( |
| <motion.div |
| initial={{ opacity: 0, y: 24, scale: 0.97 }} |
| animate={{ opacity: 1, y: 0, scale: 1 }} |
| transition={{ duration: 0.7, ease: [0.25, 0.1, 0.25, 1] as const, delay: 0.2 }} |
| className="mt-10 mb-4" |
| > |
| <div className="text-center mb-6"> |
| <span className="text-lg font-bold uppercase tracking-widest text-indigo-400" style={{ filter: 'drop-shadow(0 0 10px rgba(99,102,241,0.5))' }}> |
| DENORMALIZATION |
| </span> |
| <p className="text-sm text-slate-400 mt-2">Deliberate act of adding redundant data to speed up reads and simplify queries</p> |
| </div> |
| |
| <svg |
| viewBox="0 0 900 580" |
| className="w-full max-w-6xl mx-auto" |
| style={{ filter: 'drop-shadow(0 0 30px rgba(99,102,241,0.1))' }} |
| > |
| <defs> |
| <marker id="denormArrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"> |
| <polygon points="0 0,8 3,0 6" fill="#3b82f6" /> |
| </marker> |
| <linearGradient id="headerGrad" x1="0%" y1="0%" x2="100%" y2="0%"> |
| <stop offset="0%" stopColor="rgba(59,130,246,0.2)" /> |
| <stop offset="100%" stopColor="rgba(59,130,246,0.05)" /> |
| </linearGradient> |
| <linearGradient id="greenBoxGrad" x1="0%" y1="0%" x2="100%" y2="100%"> |
| <stop offset="0%" stopColor="rgba(34,197,94,0.12)" /> |
| <stop offset="100%" stopColor="rgba(34,197,94,0.04)" /> |
| </linearGradient> |
| <linearGradient id="redBoxGrad" x1="0%" y1="0%" x2="100%" y2="100%"> |
| <stop offset="0%" stopColor="rgba(239,68,68,0.12)" /> |
| <stop offset="100%" stopColor="rgba(239,68,68,0.04)" /> |
| </linearGradient> |
| </defs> |
| |
| {/* === HEADER BOX === */} |
| <g transform="translate(20, 10)"> |
| <rect x="0" y="0" width="860" height="55" rx="12" fill="url(#headerGrad)" stroke="rgba(59,130,246,0.4)" strokeWidth="2" /> |
| {/* Database Icon */} |
| <rect x="15" y="10" width="35" height="35" rx="6" fill="rgba(59,130,246,0.2)" stroke="#3b82f6" strokeWidth="2" /> |
| <line x1="15" y1="20" x2="50" y2="20" stroke="#3b82f6" strokeWidth="2" /> |
| <line x1="15" y1="30" x2="50" y2="30" stroke="#3b82f6" strokeWidth="2" /> |
| <line x1="15" y1="40" x2="50" y2="40" stroke="#3b82f6" strokeWidth="2" /> |
| {/* Text */} |
| <text x="65" y="22" fill="#60a5fa" fontSize="9" fontFamily="'Inter',sans-serif">Denormalization is the deliberate act of adding redundant data to a previously normalized schema</text> |
| <text x="65" y="35" fill="#60a5fa" fontSize="9" fontFamily="'Inter',sans-serif">to speed up reads and simplify queries.</text> |
| <text x="65" y="48" fill="#93c5fd" fontSize="8" fontWeight="600" fontFamily="'Inter',sans-serif">It's a targeted performance optimization, not an excuse to skip good modelling.</text> |
| </g> |
| |
| {/* === 1) NORMALIZED (Left) === */} |
| <g transform="translate(20, 80)"> |
| <rect x="0" y="0" width="380" height="320" rx="12" fill="rgba(59,130,246,0.05)" stroke="rgba(59,130,246,0.3)" strokeWidth="1.5" /> |
| |
| {/* Title */} |
| <text x="20" y="25" fill="#3b82f6" fontSize="11" fontWeight="800" fontFamily="'Inter',sans-serif">1) NORMALIZED (SOURCE OF TRUTH)</text> |
| |
| {/* Customers Icon */} |
| <g transform="translate(20, 40)"> |
| <circle cx="10" cy="8" r="5" fill="rgba(59,130,246,0.3)" stroke="#3b82f6" strokeWidth="1.5" /> |
| <ellipse cx="10" cy="20" rx="8" ry="5" fill="none" stroke="#3b82f6" strokeWidth="1.5" /> |
| <circle cx="28" cy="8" r="5" fill="rgba(59,130,246,0.3)" stroke="#3b82f6" strokeWidth="1.5" /> |
| <ellipse cx="28" cy="20" rx="8" ry="5" fill="none" stroke="#3b82f6" strokeWidth="1.5" /> |
| </g> |
| |
| {/* Customers Table */} |
| <text x="60" y="55" fill="#60a5fa" fontSize="9" fontWeight="700" fontFamily="'Inter',sans-serif">customers</text> |
| <rect x="60" y="60" width="300" height="70" rx="4" fill="rgba(15,23,42,0.4)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| {/* Header */} |
| <rect x="60" y="60" width="70" height="20" fill="rgba(59,130,246,0.2)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| <text x="95" y="73" textAnchor="middle" fill="#93c5fd" fontSize="7" fontFamily="'Inter',sans-serif">customer_id</text> |
| <text x="95" y="80" textAnchor="middle" fill="#3b82f6" fontSize="6" fontFamily="'Inter',sans-serif">(PK)</text> |
| <rect x="130" y="60" width="130" height="20" fill="rgba(59,130,246,0.2)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| <text x="195" y="73" textAnchor="middle" fill="#93c5fd" fontSize="7" fontFamily="'Inter',sans-serif">name</text> |
| <text x="195" y="80" textAnchor="middle" fill="#ef4444" fontSize="6" fontFamily="'Inter',sans-serif">(NOT NULL)</text> |
| <rect x="260" y="60" width="100" height="20" fill="rgba(59,130,246,0.2)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| <text x="310" y="73" textAnchor="middle" fill="#93c5fd" fontSize="7" fontFamily="'Inter',sans-serif">tier</text> |
| <text x="310" y="80" textAnchor="middle" fill="#ef4444" fontSize="6" fontFamily="'Inter',sans-serif">(PK)</text> |
| {/* Data */} |
| <text x="95" y="98" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">1</text> |
| <text x="195" y="98" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">Alice</text> |
| <text x="310" y="98" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">Gold</text> |
| <text x="95" y="113" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">2</text> |
| <text x="195" y="113" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">Bob</text> |
| <text x="310" y="113" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">Silver</text> |
| <text x="95" y="128" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">3</text> |
| <text x="195" y="128" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">Carol</text> |
| <text x="310" y="128" textAnchor="middle" fill="#e2e8f0" fontSize="8" fontFamily="'Inter',sans-serif">Bronze</text> |
| |
| {/* Orders Icon */} |
| <g transform="translate(20, 145)"> |
| <rect x="0" y="0" width="35" height="25" rx="3" fill="none" stroke="#3b82f6" strokeWidth="1.5" /> |
| <line x1="0" y1="8" x2="35" y2="8" stroke="#3b82f6" strokeWidth="1.5" /> |
| <line x1="0" y1="16" x2="35" y2="16" stroke="#3b82f6" strokeWidth="1.5" /> |
| <circle cx="28" cy="22" r="6" fill="none" stroke="#3b82f6" strokeWidth="1.5" /> |
| <line x1="25" y1="22" x2="31" y2="22" stroke="#3b82f6" strokeWidth="1" /> |
| <line x1="28" y1="19" x2="28" y2="25" stroke="#3b82f6" strokeWidth="1" /> |
| </g> |
| |
| {/* Orders Table */} |
| <text x="60" y="160" fill="#60a5fa" fontSize="9" fontWeight="700" fontFamily="'Inter',sans-serif">orders</text> |
| <rect x="60" y="165" width="300" height="85" rx="4" fill="rgba(15,23,42,0.4)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| {/* Header */} |
| <rect x="60" y="165" width="60" height="20" fill="rgba(59,130,246,0.2)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| <text x="90" y="178" textAnchor="middle" fill="#93c5fd" fontSize="7" fontFamily="'Inter',sans-serif">order_id</text> |
| <text x="90" y="185" textAnchor="middle" fill="#3b82f6" fontSize="6" fontFamily="'Inter',sans-serif">(PK)</text> |
| <rect x="120" y="165" width="70" height="20" fill="rgba(59,130,246,0.2)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| <text x="155" y="178" textAnchor="middle" fill="#93c5fd" fontSize="7" fontFamily="'Inter',sans-serif">customer_id</text> |
| <text x="155" y="185" textAnchor="middle" fill="#3b82f6" fontSize="6" fontFamily="'Inter',sans-serif">(FK)</text> |
| <rect x="190" y="165" width="85" height="20" fill="rgba(59,130,246,0.2)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| <text x="232" y="178" textAnchor="middle" fill="#93c5fd" fontSize="7" fontFamily="'Inter',sans-serif">order_total</text> |
| <text x="232" y="185" textAnchor="middle" fill="#64748b" fontSize="6" fontFamily="'Inter',sans-serif">(NUMERIC(12,2))</text> |
| <rect x="275" y="165" width="85" height="20" fill="rgba(59,130,246,0.2)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| <text x="317" y="178" textAnchor="middle" fill="#93c5fd" fontSize="7" fontFamily="'Inter',sans-serif">created_at</text> |
| {/* Data */} |
| <text x="90" y="203" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">101</text> |
| <text x="155" y="203" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">1</text> |
| <text x="232" y="203" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">120.00</text> |
| <text x="317" y="203" textAnchor="middle" fill="#e2e8f0" fontSize="6" fontFamily="'Inter',sans-serif">2025-05-01 10:15:00</text> |
| <text x="90" y="218" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">102</text> |
| <text x="155" y="218" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">2</text> |
| <text x="232" y="218" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">85.50</text> |
| <text x="317" y="218" textAnchor="middle" fill="#e2e8f0" fontSize="6" fontFamily="'Inter',sans-serif">2025-05-01 11:20:00</text> |
| <text x="90" y="233" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">103</text> |
| <text x="155" y="233" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">1</text> |
| <text x="232" y="233" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">230.00</text> |
| <text x="317" y="233" textAnchor="middle" fill="#e2e8f0" fontSize="6" fontFamily="'Inter',sans-serif">2025-05-02 09:30:00</text> |
| <text x="90" y="248" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">104</text> |
| <text x="155" y="248" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">3</text> |
| <text x="232" y="248" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">75.00</text> |
| <text x="317" y="248" textAnchor="middle" fill="#e2e8f0" fontSize="6" fontFamily="'Inter',sans-serif">2025-05-02 12:45:00</text> |
| </g> |
| |
| {/* Arrow between sections */} |
| <line x1="410" y1="240" x2="430" y2="240" stroke="#3b82f6" strokeWidth="2" markerEnd="url(#denormArrow)" /> |
| |
| {/* === 2) TYPICAL REPORT (Bottom Left) === */} |
| <g transform="translate(20, 410)"> |
| <rect x="0" y="0" width="380" height="70" rx="12" fill="rgba(34,197,94,0.08)" stroke="rgba(34,197,94,0.3)" strokeWidth="1.5" /> |
| <text x="15" y="20" fill="#4ade80" fontSize="10" fontWeight="800" fontFamily="'Inter',sans-serif">2) TYPICAL REPORT NEEDS A JOIN</text> |
| <text x="15" y="38" fill="#86efac" fontSize="7" fontFamily="'monospace',sans-serif">SELECT c.name, c.tier, SUM(o.order_total) AS revenue</text> |
| <text x="15" y="50" fill="#86efac" fontSize="7" fontFamily="'monospace',sans-serif">FROM orders o</text> |
| <text x="15" y="62" fill="#86efac" fontSize="7" fontFamily="'monospace',sans-serif">JOIN customers c ON c.customer_id = o.customer_id GROUP BY c.name, c.tier;</text> |
| </g> |
| |
| {/* === 3) DENORMALIZED (Right) === */} |
| <g transform="translate(440, 80)"> |
| <rect x="0" y="0" width="440" height="320" rx="12" fill="rgba(34,197,94,0.05)" stroke="rgba(34,197,94,0.3)" strokeWidth="1.5" /> |
| |
| {/* Title */} |
| <text x="15" y="25" fill="#22c55e" fontSize="11" fontWeight="800" fontFamily="'Inter',sans-serif">3) DENORMALIZED (FOR READ PERFORMANCE)</text> |
| |
| {/* Subtitle */} |
| <text x="15" y="42" fill="#4ade80" fontSize="8" fontFamily="'Inter',sans-serif">orders_enriched (with redundant customer data)</text> |
| |
| {/* Enriched Table */} |
| <rect x="15" y="50" width="410" height="125" rx="4" fill="rgba(15,23,42,0.4)" stroke="rgba(34,197,94,0.3)" strokeWidth="1" /> |
| {/* Header */} |
| <rect x="15" y="50" width="50" height="25" fill="rgba(34,197,94,0.2)" stroke="rgba(34,197,94,0.3)" strokeWidth="1" /> |
| <text x="40" y="63" textAnchor="middle" fill="#86efac" fontSize="6" fontFamily="'Inter',sans-serif">order_id</text> |
| <text x="40" y="72" textAnchor="middle" fill="#22c55e" fontSize="5" fontFamily="'Inter',sans-serif">(PK)</text> |
| <rect x="65" y="50" width="60" height="25" fill="rgba(34,197,94,0.2)" stroke="rgba(34,197,94,0.3)" strokeWidth="1" /> |
| <text x="95" y="67" textAnchor="middle" fill="#86efac" fontSize="6" fontFamily="'Inter',sans-serif">customer_id</text> |
| <text x="95" y="74" textAnchor="middle" fill="#3b82f6" fontSize="5" fontFamily="'Inter',sans-serif">(FK)</text> |
| <rect x="125" y="50" width="90" height="25" fill="rgba(245,158,11,0.15)" stroke="#f59e0b" strokeWidth="1" /> |
| <text x="170" y="63" textAnchor="middle" fill="#fbbf24" fontSize="6" fontFamily="'Inter',sans-serif">customer_name</text> |
| <text x="170" y="72" textAnchor="middle" fill="#f59e0b" fontSize="5" fontFamily="'Inter',sans-serif">(DENORMALIZED)</text> |
| <rect x="215" y="50" width="70" height="25" fill="rgba(245,158,11,0.15)" stroke="#f59e0b" strokeWidth="1" /> |
| <text x="250" y="63" textAnchor="middle" fill="#fbbf24" fontSize="6" fontFamily="'Inter',sans-serif">customer_tier</text> |
| <text x="250" y="72" textAnchor="middle" fill="#f59e0b" fontSize="5" fontFamily="'Inter',sans-serif">(DENORMALIZED)</text> |
| <rect x="285" y="50" width="70" height="25" fill="rgba(34,197,94,0.2)" stroke="rgba(34,197,94,0.3)" strokeWidth="1" /> |
| <text x="320" y="63" textAnchor="middle" fill="#86efac" fontSize="6" fontFamily="'Inter',sans-serif">order_total</text> |
| <text x="320" y="72" textAnchor="middle" fill="#64748b" fontSize="5" fontFamily="'Inter',sans-serif">(NUMERIC(12,2))</text> |
| <rect x="355" y="50" width="70" height="25" fill="rgba(34,197,94,0.2)" stroke="rgba(34,197,94,0.3)" strokeWidth="1" /> |
| <text x="390" y="67" textAnchor="middle" fill="#86efac" fontSize="6" fontFamily="'Inter',sans-serif">created_at</text> |
| {/* Data */} |
| <text x="40" y="93" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">101</text> |
| <text x="95" y="93" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">1</text> |
| <text x="170" y="93" textAnchor="middle" fill="#fbbf24" fontSize="7" fontFamily="'Inter',sans-serif">Alice</text> |
| <text x="250" y="93" textAnchor="middle" fill="#fbbf24" fontSize="7" fontFamily="'Inter',sans-serif">Gold</text> |
| <text x="320" y="93" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">120.00</text> |
| <text x="390" y="93" textAnchor="middle" fill="#e2e8f0" fontSize="6" fontFamily="'Inter',sans-serif">2025-05-01 10:15:00</text> |
| <text x="40" y="108" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">102</text> |
| <text x="95" y="108" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">2</text> |
| <text x="170" y="108" textAnchor="middle" fill="#fbbf24" fontSize="7" fontFamily="'Inter',sans-serif">Bob</text> |
| <text x="250" y="108" textAnchor="middle" fill="#fbbf24" fontSize="7" fontFamily="'Inter',sans-serif">Silver</text> |
| <text x="320" y="108" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">85.50</text> |
| <text x="390" y="108" textAnchor="middle" fill="#e2e8f0" fontSize="6" fontFamily="'Inter',sans-serif">2025-05-01 11:20:00</text> |
| <text x="40" y="123" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">103</text> |
| <text x="95" y="123" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">1</text> |
| <text x="170" y="123" textAnchor="middle" fill="#fbbf24" fontSize="7" fontFamily="'Inter',sans-serif">Alice</text> |
| <text x="250" y="123" textAnchor="middle" fill="#fbbf24" fontSize="7" fontFamily="'Inter',sans-serif">Gold</text> |
| <text x="320" y="123" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">230.00</text> |
| <text x="390" y="123" textAnchor="middle" fill="#e2e8f0" fontSize="6" fontFamily="'Inter',sans-serif">2025-05-02 09:30:00</text> |
| <text x="40" y="138" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">104</text> |
| <text x="95" y="138" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">3</text> |
| <text x="170" y="138" textAnchor="middle" fill="#fbbf24" fontSize="7" fontFamily="'Inter',sans-serif">Carol</text> |
| <text x="250" y="138" textAnchor="middle" fill="#fbbf24" fontSize="7" fontFamily="'Inter',sans-serif">Bronze</text> |
| <text x="320" y="138" textAnchor="middle" fill="#e2e8f0" fontSize="7" fontFamily="'Inter',sans-serif">75.00</text> |
| <text x="390" y="138" textAnchor="middle" fill="#e2e8f0" fontSize="6" fontFamily="'Inter',sans-serif">2025-05-02 12:45:00</text> |
| |
| {/* Success message */} |
| <rect x="15" y="185" width="410" height="55" rx="6" fill="rgba(34,197,94,0.1)" stroke="#22c55e" strokeWidth="1" /> |
| <text x="30" y="200" fill="#4ade80" fontSize="9">✓</text> |
| <text x="50" y="202" fill="#4ade80" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">Now the same report is simpler and faster (no join needed):</text> |
| <text x="50" y="218" fill="#86efac" fontSize="7" fontFamily="'monospace',sans-serif">SELECT customer_name, customer_tier, SUM(order_total) AS revenue</text> |
| <text x="50" y="230" fill="#86efac" fontSize="7" fontFamily="'monospace',sans-serif">FROM orders_enriched</text> |
| <text x="50" y="242" fill="#86efac" fontSize="7" fontFamily="'monospace',sans-serif">GROUP BY customer_name, customer_tier;</text> |
| |
| {/* SQL Source of Truth */} |
| <text x="15" y="260" fill="#60a5fa" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">SQL: SOURCE OF TRUTH (NORMALIZED)</text> |
| <rect x="15" y="268" width="200" height="45" rx="4" fill="rgba(15,23,42,0.4)" stroke="rgba(59,130,246,0.3)" strokeWidth="1" /> |
| <text x="25" y="280" fill="#60a5fa" fontSize="6" fontFamily="'monospace',sans-serif">-- Source of truth</text> |
| <text x="25" y="290" fill="#93c5fd" fontSize="6" fontFamily="'monospace',sans-serif">CREATE TABLE customers (</text> |
| <text x="35" y="300" fill="#fbbf24" fontSize="6" fontFamily="'monospace',sans-serif">customer_id BIGINT PRIMARY KEY,</text> |
| <text x="35" y="308" fill="#fbbf24" fontSize="6" fontFamily="'monospace',sans-serif">name TEXT NOT NULL,</text> |
| </g> |
| |
| {/* === PROS (Bottom Left) === */} |
| <g transform="translate(20, 490)"> |
| <rect x="0" y="0" width="260" height="80" rx="12" fill="url(#greenBoxGrad)" stroke="rgba(34,197,94,0.4)" strokeWidth="2" /> |
| <rect x="0" y="0" width="260" height="22" rx="12" fill="#22c55e" /> |
| <rect x="0" y="12" width="260" height="68" rx="0" fill="rgba(34,197,94,0.05)" /> |
| <text x="130" y="15" textAnchor="middle" fill="white" fontSize="9" fontWeight="800" fontFamily="'Inter',sans-serif">PROS: WHAT YOU GAIN</text> |
| |
| {/* Rocket Icon */} |
| <path d="M15 35 L20 30 L25 35 L22 35 L22 48 L18 48 L18 35 Z" fill="none" stroke="#4ade80" strokeWidth="1.5" /> |
| <text x="35" y="40" fill="#4ade80" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">Faster reads</text> |
| <text x="35" y="52" fill="#86efac" fontSize="7" fontFamily="'Inter',sans-serif">sub-p99 latencies</text> |
| |
| {/* Search Icon */} |
| <circle cx="20" cy="62" r="5" fill="none" stroke="#4ade80" strokeWidth="1.5" /> |
| <line x1="24" y1="66" x2="28" y2="70" stroke="#4ade80" strokeWidth="1.5" strokeLinecap="round" /> |
| <text x="35" y="67" fill="#4ade80" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">Simpler queries</text> |
| <text x="35" y="79" fill="#86efac" fontSize="7" fontFamily="'Inter',sans-serif">fewer joins/aggregations</text> |
| </g> |
| |
| {/* === CONS (Bottom Center) === */} |
| <g transform="translate(300, 490)"> |
| <rect x="0" y="0" width="280" height="80" rx="12" fill="url(#redBoxGrad)" stroke="rgba(239,68,68,0.4)" strokeWidth="2" /> |
| <rect x="0" y="0" width="280" height="22" rx="12" fill="#ef4444" /> |
| <rect x="0" y="12" width="280" height="68" rx="0" fill="rgba(239,68,68,0.05)" /> |
| <text x="140" y="15" textAnchor="middle" fill="white" fontSize="9" fontWeight="800" fontFamily="'Inter',sans-serif">CONS: WHAT IT COSTS</text> |
| |
| {/* Edit Icon */} |
| <path d="M15 35 L22 28 L25 31 L18 38 Z" fill="none" stroke="#f87171" strokeWidth="1.5" /> |
| <line x1="12" y1="40" x2="18" y2="38" stroke="#f87171" strokeWidth="1.5" /> |
| <text x="35" y="40" fill="#f87171" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">Write amplification</text> |
| <text x="35" y="52" fill="#fca5a5" fontSize="7" fontFamily="'Inter',sans-serif">extra updates/inserts</text> |
| |
| {/* Warning Icon */} |
| <path d="M20 60 L25 70 L15 70 Z" fill="none" stroke="#f87171" strokeWidth="1.5" /> |
| <text x="20" y="68" textAnchor="middle" fill="#f87171" fontSize="8">!</text> |
| <text x="35" y="67" fill="#f87171" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">Consistency risk</text> |
| <text x="35" y="79" fill="#fca5a5" fontSize="7" fontFamily="'Inter',sans-serif">stale/drifting data</text> |
| </g> |
| |
| {/* === OPERATIONAL (Bottom Right) === */} |
| <g transform="translate(600, 490)"> |
| <rect x="0" y="0" width="280" height="80" rx="12" fill="rgba(245,158,11,0.08)" stroke="rgba(245,158,11,0.4)" strokeWidth="2" /> |
| |
| {/* Gear Icon */} |
| <circle cx="20" cy="25" r="10" fill="none" stroke="#fbbf24" strokeWidth="1.5" /> |
| <circle cx="20" cy="25" r="4" fill="none" stroke="#fbbf24" strokeWidth="1.5" /> |
| <line x1="20" y1="10" x2="20" y2="14" stroke="#fbbf24" strokeWidth="1.5" /> |
| <line x1="20" y1="36" x2="20" y2="40" stroke="#fbbf24" strokeWidth="1.5" /> |
| <line x1="5" y1="25" x2="9" y2="25" stroke="#fbbf24" strokeWidth="1.5" /> |
| <line x1="31" y1="25" x2="35" y2="25" stroke="#fbbf24" strokeWidth="1.5" /> |
| <text x="40" y="28" fill="#fbbf24" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">Operational overhead</text> |
| <text x="40" y="40" fill="#fcd34d" fontSize="7" fontFamily="'Inter',sans-serif">(refreshes, backfills, monitoring)</text> |
| |
| {/* Fan-out Icon */} |
| <path d="M15 58 L20 65 L25 58" fill="none" stroke="#fbbf24" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> |
| <line x1="20" y1="52" x2="20" y2="58" stroke="#fbbf24" strokeWidth="1.5" /> |
| <line x1="12" y1="55" x2="16" y2="58" stroke="#fbbf24" strokeWidth="1.5" /> |
| <line x1="28" y1="55" x2="24" y2="58" stroke="#fbbf24" strokeWidth="1.5" /> |
| <text x="35" y="62" fill="#fbbf24" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">Change fan-out</text> |
| <text x="35" y="74" fill="#fcd34d" fontSize="7" fontFamily="'Inter',sans-serif">schema/logic updates must be mirrored</text> |
| </g> |
| |
| {/* === EXAMPLE Badge === */} |
| <rect x="180" y="75" width="60" height="18" rx="9" fill="#3b82f6" /> |
| <text x="210" y="87" textAnchor="middle" fill="white" fontSize="8" fontWeight="700" fontFamily="'Inter',sans-serif">EXAMPLE</text> |
| </svg> |
| </motion.div> |
| ); |
| } |
|
|