{"id":49865941,"url":"https://github.com/lukasniessen/relational-db-vs-document-store","last_synced_at":"2026-05-15T03:00:23.632Z","repository":{"id":293858546,"uuid":"985336364","full_name":"LukasNiessen/relational-db-vs-document-store","owner":"LukasNiessen","description":"Relational DBs vs Document-Oriented DBs for system design","archived":false,"fork":false,"pushed_at":"2025-05-18T14:56:53.000Z","size":54,"stargazers_count":19,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-05-15T03:00:04.568Z","etag":null,"topics":["database","document-store","documentdb","mongodb","postgres","postgresql","relational-databases","rtdb","software-architecture","system-design"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LukasNiessen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-17T14:49:49.000Z","updated_at":"2025-11-03T21:47:47.000Z","dependencies_parsed_at":"2026-05-15T03:00:18.972Z","dependency_job_id":null,"html_url":"https://github.com/LukasNiessen/relational-db-vs-document-store","commit_stats":null,"previous_names":["lukasniessen/relational-db-vs-document-store"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/LukasNiessen/relational-db-vs-document-store","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Frelational-db-vs-document-store","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Frelational-db-vs-document-store/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Frelational-db-vs-document-store/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Frelational-db-vs-document-store/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LukasNiessen","download_url":"https://codeload.github.com/LukasNiessen/relational-db-vs-document-store/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Frelational-db-vs-document-store/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33051875,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-15T02:00:06.351Z","response_time":103,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","document-store","documentdb","mongodb","postgres","postgresql","relational-databases","rtdb","software-architecture","system-design"],"created_at":"2026-05-15T03:00:12.262Z","updated_at":"2026-05-15T03:00:23.623Z","avatar_url":"https://github.com/LukasNiessen.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Relational vs Document-Oriented Database for Software Architecture\n\nWhat I go through in here is:\n\n1. Super quick refresher of what these two are\n2. Key differences\n3. Strengths and weaknesses\n4. System design examples (+ Spring Java code)\n5. Brief history\n\nIn the examples, I choose a relational DB in the first, and a document-oriented DB in the other. The focus is on _why_ did I make that choice. I also provide some example code for both.\n\nIn the strengths and weaknesses part, I discuss both what _used to be a strength/weakness_ and how it looks nowadays.\n\n## Super short summary\n\nThe two most common types of DBs are:\n\n- **Relational database (RDB)**: PostgreSQL, MySQL, MSSQL, Oracle DB, ...\n- **Document-oriented database (document store):** MongoDB, DynamoDB, CouchDB...\n\n### RDB\n\nThe key idea is: **fit the data into a table**. The columns are _properties_ and the rows are the _values_. By doing this, we have our data in a very structured way. So we have much power for querying the data (using SQL). That is, we can do all sorts of filters, joints etc. The _way_ we arrange the data into the table is called the _database schema_.\n\n#### Example table\n\n```\n+----+---------+---------------------+-----+\n| ID | Name    | Email               | Age |\n+----+---------+---------------------+-----+\n| 1  | Alice   | alice@example.com   | 30  |\n| 2  | Bob     | bob@example.com     | 25  |\n| 3  | Charlie | charlie@example.com | 28  |\n+----+---------+---------------------+-----+\n```\n\nA database can have many tables.\n\n### Document stores\n\nThe key idea is: **just store the data as it is**. Suppose we have an object. We just convert it to a JSON and store it as it is. We call this data a _document_. It's not limited to JSON though, it can also be BSON (binary JSON) or XML for example.\n\n#### Example document\n\n```JSON\n{\n  \"user_id\": 123,\n  \"name\": \"Alice\",\n  \"email\": \"alice@example.com\",\n  \"orders\": [\n    {\"id\": 1, \"item\": \"Book\", \"price\": 12.99},\n    {\"id\": 2, \"item\": \"Pen\", \"price\": 1.50}\n  ]\n}\n```\n\nEach document is saved under a unique ID. This ID can be a path, for example in Google Cloud Firestore, but doesn't have to be.\n\nMany documents _'in the same bucket'_ is called a _collection_. We can have many collections.\n\n## Differences\n\n#### Schema\n\n- RDBs have a fixed schema. Every row _'has the same schema'_.\n- Document stores don't have schemas. Each document can _'have a different schema'_.\n\n#### Data Structure\n\n- RDBs break data into normalized tables with relationships through foreign keys\n- Document stores nest related data directly within documents as embedded objects or arrays\n\n#### Query Language\n\n- RDBs use SQL, a standardized declarative language\n- Document stores typically have their own query APIs\n  - Nowadays, the common document stores support SQL-like queries too\n\n#### Scaling Approach\n\n- RDBs traditionally scale vertically (bigger/better machines)\n  - Nowadays, the most common RDBs offer horizontal scaling as well (eg. PostgeSQL)\n- Document stores are great for horizontal scaling (more machines)\n\n#### Transaction Support\n\nACID = availability, consistency, isolation, durability\n\n- RDBs have mature ACID transaction support\n- Document stores traditionally sacrificed ACID guarantees in favor of performance and availability\n  - The most common document stores nowadays support ACID though (eg. MongoDB)\n\n## Strengths, weaknesses\n\n### Relational Databases\n\nI want to repeat a few things here again that have changed. As noted, nowadays, most document stores support SQL and ACID. Likewise, most RDBs nowadays support horizontal scaling.\n\nHowever, let's look at ACID for example. While document stores support it, it's much more mature in RDBs. So if your app puts super high relevance on ACID, then probably RDBs are better. But if your app just needs basic ACID, both works well and this shouldn't be the deciding factor.\n\nFor this reason, I have put these points, that are supported in both, in **parentheses**.\n\n**Strengths:**\n\n- **Data Integrity**: Strong schema enforcement ensures data consistency\n- (**Complex Querying**: Great for complex joins and aggregations across multiple tables)\n- (**ACID**)\n\n**Weaknesses:**\n\n- **Schema**: While the schema was listed as a strength, it also is a weakness. Changing the schema requires migrations which can be painful\n- **Object-Relational Impedance Mismatch**: Translating between application objects and relational tables adds complexity. Hibernate and other Object-relational mapping (ORM) frameworks help though.\n- (**Horizontal Scaling**: Supported but sharding is more complex as compared to document stores)\n- **Initial Dev Speed**: Setting up schemas etc takes some time\n\n### Document-Oriented Databases\n\n**Strengths:**\n\n- **Schema Flexibility**: Better for heterogeneous data structures\n- **Throughput:** Supports high throughput, especially write throughput\n- (**Horizontal Scaling**: Horizontal scaling is easier, you can shard document-wise (document 1-1000 on computer A and 1000-2000 on computer B))\n- **Performance for Document-Based Access**: Retrieving or updating an entire document is very efficient\n- **One-to-Many Relationships**: Superior in this regard. You don't need joins or other operations.\n- **Locality**: See below\n- **Initial Dev Speed**: Getting started is quicker due to the flexibility\n\n**Weaknesses:**\n\n- **Complex Relationships**: Many-to-one and many-to-many relationships are difficult and often require denormalization or application-level joins\n- **Data Consistency**: More responsibility falls on application code to maintain data integrity\n- **Query Optimization**: Less mature optimization engines compared to relational systems\n- **Storage Efficiency**: Potential data duplication increases storage requirements\n- **Locality**: See below\n\n### Locality\n\nI have listed locality as a strength and a weakness of document stores. Here is what I mean with this.\n\nIn document stores, cocuments are typically stored as a single, continuous string, encoded in formats like JSON, XML, or binary variants such as MongoDB's BSON. This structure provides a locality advantage when applications need to access entire documents. Storing related data together minimizes disk seeks, unlike relational databases (RDBs) where data split across multiple tables - this requires multiple index lookups, increasing retrieval time.\n\nHowever, it's only a benefit when we need (almost) the entire document at once. Document stores typically load the entire document, even if only a small part is accessed. This is inefficient for large documents. Similarly, updates often require rewriting the entire document. So to keep these downsides small, make sure your documents are small.\n\nLast note: Locality isn't exclusive to document stores. For example Google Spanner or Oracle achieve a similar locality in a relational model.\n\n## System Design Examples\n\nNote that I limit the examples to the minimum so the article is not totally bloated. The code is incomplete on purpose. You can find the complete code in the examples folder of the repo.\n\nThe examples folder contains two complete applications:\n\n1. `financial-transaction-system` - A Spring Boot and React application using a relational database (H2)\n2. `content-management-system` - A Spring Boot and React application using a document-oriented database (MongoDB)\n\nEach example has its own README file with instructions for running the applications.\n\n## Example 1: Financial Transaction System\n\n### Requirements\n\n#### Functional requirements\n\n- Process payments and transfers\n- Maintain accurate account balances\n- Store audit trails for all operations\n\n#### Non-functional requirements\n\n- Reliability (!!)\n- Data consistency (!!)\n\n#### Why Relational is Better Here\n\nWe want reliability and data consistency. Though document stores support this too (ACID for example), they are less mature in this regard. The benefits of document stores are not interesting for us, so we go with an RDB.\n\nNote: If we would expand this example and add things like _profiles of sellers_, _ratings_ and more, we might want to add a separate DB where we have different priorities such as availability and high throughput. With two separate DBs we can support different requirements and scale them independently.\n\n### Data Model\n\n```\nAccounts:\n- account_id (PK = Primary Key)\n- customer_id (FK = Foreign Key)\n- account_type\n- balance\n- created_at\n- status\n\nTransactions:\n- transaction_id (PK)\n- from_account_id (FK)\n- to_account_id (FK)\n- amount\n- type\n- status\n- created_at\n- reference_number\n```\n\n### Spring Boot Implementation\n\n```java\n// Entity classes\n@Entity\n@Table(name = \"accounts\")\npublic class Account {\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long accountId;\n\n    @Column(nullable = false)\n    private Long customerId;\n\n    @Column(nullable = false)\n    private String accountType;\n\n    @Column(nullable = false)\n    private BigDecimal balance;\n\n    @Column(nullable = false)\n    private LocalDateTime createdAt;\n\n    @Column(nullable = false)\n    private String status;\n\n    // Getters and setters\n}\n\n@Entity\n@Table(name = \"transactions\")\npublic class Transaction {\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long transactionId;\n\n    @ManyToOne\n    @JoinColumn(name = \"from_account_id\")\n    private Account fromAccount;\n\n    @ManyToOne\n    @JoinColumn(name = \"to_account_id\")\n    private Account toAccount;\n\n    @Column(nullable = false)\n    private BigDecimal amount;\n\n    @Column(nullable = false)\n    private String type;\n\n    @Column(nullable = false)\n    private String status;\n\n    @Column(nullable = false)\n    private LocalDateTime createdAt;\n\n    @Column(nullable = false)\n    private String referenceNumber;\n\n    // Getters and setters\n}\n\n// Repository\npublic interface TransactionRepository extends JpaRepository\u003cTransaction, Long\u003e {\n    List\u003cTransaction\u003e findByFromAccountAccountIdOrToAccountAccountId(Long accountId, Long sameAccountId);\n    List\u003cTransaction\u003e findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);\n}\n\n// Service with transaction support\n@Service\npublic class TransferService {\n    private final AccountRepository accountRepository;\n    private final TransactionRepository transactionRepository;\n\n    @Autowired\n    public TransferService(AccountRepository accountRepository, TransactionRepository transactionRepository) {\n        this.accountRepository = accountRepository;\n        this.transactionRepository = transactionRepository;\n    }\n\n    @Transactional\n    public Transaction transferFunds(Long fromAccountId, Long toAccountId, BigDecimal amount) {\n        Account fromAccount = accountRepository.findById(fromAccountId)\n                .orElseThrow(() -\u003e new AccountNotFoundException(\"Source account not found\"));\n\n        Account toAccount = accountRepository.findById(toAccountId)\n                .orElseThrow(() -\u003e new AccountNotFoundException(\"Destination account not found\"));\n\n        if (fromAccount.getBalance().compareTo(amount) \u003c 0) {\n            throw new InsufficientFundsException(\"Insufficient funds in source account\");\n        }\n\n        // Update balances\n        fromAccount.setBalance(fromAccount.getBalance().subtract(amount));\n        toAccount.setBalance(toAccount.getBalance().add(amount));\n\n        accountRepository.save(fromAccount);\n        accountRepository.save(toAccount);\n\n        // Create transaction record\n        Transaction transaction = new Transaction();\n        transaction.setFromAccount(fromAccount);\n        transaction.setToAccount(toAccount);\n        transaction.setAmount(amount);\n        transaction.setType(\"TRANSFER\");\n        transaction.setStatus(\"COMPLETED\");\n        transaction.setCreatedAt(LocalDateTime.now());\n        transaction.setReferenceNumber(generateReferenceNumber());\n\n        return transactionRepository.save(transaction);\n    }\n\n    private String generateReferenceNumber() {\n        return \"TXN\" + System.currentTimeMillis();\n    }\n}\n```\n\n## System Design Example 2: Content Management System\n\nA content management system.\n\n### Requirements\n\n- Store various content types, including articles and products\n- Allow adding new content types\n- Support comments\n\n### Non-functional requirements\n\n- Performance\n- Availability\n- Elasticity\n\n### Why Document Store is Better Here\n\nAs we have no critical transaction like in the previous example but are only interested in performance, availability and elasticity, document stores are a great choice. Considering that various content types is a requirement, our life is easier with document stores as they are schema-less.\n\n### Data Model\n\n```json\n// Article document\n{\n  \"id\": \"article123\",\n  \"type\": \"article\",\n  \"title\": \"Understanding NoSQL\",\n  \"author\": {\n    \"id\": \"user456\",\n    \"name\": \"Jane Smith\",\n    \"email\": \"jane@example.com\"\n  },\n  \"content\": \"Lorem ipsum dolor sit amet...\",\n  \"tags\": [\"database\", \"nosql\", \"tutorial\"],\n  \"published\": true,\n  \"publishedDate\": \"2025-05-01T10:30:00Z\",\n  \"comments\": [\n    {\n      \"id\": \"comment789\",\n      \"userId\": \"user101\",\n      \"userName\": \"Bob Johnson\",\n      \"text\": \"Great article!\",\n      \"timestamp\": \"2025-05-02T14:20:00Z\",\n      \"replies\": [\n        {\n          \"id\": \"reply456\",\n          \"userId\": \"user456\",\n          \"userName\": \"Jane Smith\",\n          \"text\": \"Thanks Bob!\",\n          \"timestamp\": \"2025-05-02T15:45:00Z\"\n        }\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"viewCount\": 1250,\n    \"likeCount\": 42,\n    \"featuredImage\": \"/images/nosql-header.jpg\",\n    \"estimatedReadTime\": 8\n  }\n}\n\n// Product document (completely different structure)\n{\n  \"id\": \"product789\",\n  \"type\": \"product\",\n  \"name\": \"Premium Ergonomic Chair\",\n  \"price\": 299.99,\n  \"categories\": [\"furniture\", \"office\", \"ergonomic\"],\n  \"variants\": [\n    {\n      \"color\": \"black\",\n      \"sku\": \"EC-BLK-001\",\n      \"inStock\": 23\n    },\n    {\n      \"color\": \"gray\",\n      \"sku\": \"EC-GRY-001\",\n      \"inStock\": 14\n    }\n  ],\n  \"specifications\": {\n    \"weight\": \"15kg\",\n    \"dimensions\": \"65x70x120cm\",\n    \"material\": \"Mesh and aluminum\"\n  }\n}\n```\n\n### Spring Boot Implementation with MongoDB\n\n```java\n@Document(collection = \"content\")\npublic class ContentItem {\n    @Id\n    private String id;\n    private String type;\n    private Map\u003cString, Object\u003e data;\n\n    // Common fields can be explicit\n    private boolean published;\n    private Date createdAt;\n    private Date updatedAt;\n\n    // The rest can be dynamic\n    @DBRef(lazy = true)\n    private User author;\n\n    private List\u003cComment\u003e comments;\n\n    // Basic getters and setters\n}\n\n// MongoDB Repository\npublic interface ContentRepository extends MongoRepository\u003cContentItem, String\u003e {\n    List\u003cContentItem\u003e findByType(String type);\n    List\u003cContentItem\u003e findByTypeAndPublishedTrue(String type);\n    List\u003cContentItem\u003e findByData_TagsContaining(String tag);\n}\n\n// Service for content management\n@Service\npublic class ContentService {\n    private final ContentRepository contentRepository;\n\n    @Autowired\n    public ContentService(ContentRepository contentRepository) {\n        this.contentRepository = contentRepository;\n    }\n\n    public ContentItem createContent(String type, Map\u003cString, Object\u003e data, User author) {\n        ContentItem content = new ContentItem();\n        content.setType(type);\n        content.setData(data);\n        content.setAuthor(author);\n        content.setCreatedAt(new Date());\n        content.setUpdatedAt(new Date());\n        content.setPublished(false);\n\n        return contentRepository.save(content);\n    }\n\n    public ContentItem addComment(String contentId, Comment comment) {\n        ContentItem content = contentRepository.findById(contentId)\n                .orElseThrow(() -\u003e new ContentNotFoundException(\"Content not found\"));\n\n        if (content.getComments() == null) {\n            content.setComments(new ArrayList\u003c\u003e());\n        }\n\n        content.getComments().add(comment);\n        content.setUpdatedAt(new Date());\n\n        return contentRepository.save(content);\n    }\n\n    // Easily add new fields without migrations\n    public ContentItem addMetadata(String contentId, String key, Object value) {\n        ContentItem content = contentRepository.findById(contentId)\n                .orElseThrow(() -\u003e new ContentNotFoundException(\"Content not found\"));\n\n        Map\u003cString, Object\u003e data = content.getData();\n        if (data == null) {\n            data = new HashMap\u003c\u003e();\n        }\n\n        // Just update the field, no schema changes needed\n        data.put(key, value);\n        content.setData(data);\n\n        return contentRepository.save(content);\n    }\n}\n```\n\n## Brief History of RDBs vs NoSQL\n\n- Edgar Codd published a paper in 1970 proposing RDBs\n- RDBs became the leader of DBs, mainly due to their reliability\n- NoSQL emerged around 2009, companies like Facebook \u0026 Google developed custom solutions to handle their unprecedented scale. They published papers on their internal database systems, inspiring open-source alternatives like MongoDB, Cassandra, and Couchbase.\n\n  - The term itself came from a Twitter hashtag actually\n\nThe main reasons for a _'NoSQL wish'_ were:\n\n- Need for horizontal scalability\n- More flexible data models\n- Performance optimization\n- Lower operational costs\n\nHowever, as mentioned already, nowadays RDBs support these things as well, so the clear distinctions between RDBs and document stores are becoming more and more blurry. Most modern databases incorporate features from both.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasniessen%2Frelational-db-vs-document-store","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukasniessen%2Frelational-db-vs-document-store","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasniessen%2Frelational-db-vs-document-store/lists"}