DEV Community

Cover image for From ERD to DDD: 40 Years of Software Architecture (And Why I am Building TaroDomain)
Irwansyah
Irwansyah

Posted on

From ERD to DDD: 40 Years of Software Architecture (And Why I am Building TaroDomain)

This morning Bude Ti asked me a question while I was sipping Tubruk coffee at Pak Slamet’s warkop. "Taro, you've been in IT for 20 years. Why do kids these days argue so much about React vs Angular, or Monolith vs Microservices? Isn't there one right answer?"

I sipped my coffee. "Bude, those are just tools. Software architecture is like a pendulum. You swing left, right, back to the left. Searching for the perfect balance between business agility and technical scalability."

Bude Ti stirred her rice. "Pendulum? That pendulum on the wall clock is just there to cover up a nail hole."

Literal. Bude Ti is indeed the most literal interpreter of instructions on earth. But she's not wrong. For 40 years, the software industry has been treated like a pendulum. Swinging back and forth.

Here is the unvarnished history of how we got here, the trade-offs of each era, and why I finally decided to build a new framework: TaroDomain.

The 80s: ERD is King, OOP is an Afterthought

In the 80s and early 90s, designing software was viewed entirely from the database's perspective. We used DFDs (Data Flow Diagrams) to track how data moved, and ERDs (Entity-Relationship Diagrams) to design the database schema. Because databases were the most expensive and critical part of the system, the ERD became the de facto blueprint for the entire application.

Then Object-Oriented Programming (OOP) arrived. IT folks back then struggled to translate these data-centric diagrams into objects. This birthed Object-Oriented Analysis and Design (OOAD). Pioneers like Grady Booch created rigorous modeling techniques, notably the Use Case Document. By 1997, they unified their methods into UML (Unified Modeling Language). We meticulously drew Class Diagrams before writing any code. Around the same time, the Gang of Four (GoF) published their book on Design Patterns.

OOAD and UML gave us a rigorous, standardized way to bridge business requirements and code. Use Cases structured our requirements. Design Patterns gave us a shared vocabulary.

UML quickly devolved into a heavy, jarring waterfall artifact. Developers spent months drawing Class Diagrams that became obsolete the moment code was written. And because the ERD was still the foundation, developers simply created objects that mirrored database tables. OOP devolved into "classy procedural code" where objects were just dumb data bags and UML was just a pretty picture of a database schema.

The Three-Tier Era and the ORM Trap

In the late 90s and early 2000s, web apps grew massive. The tight coupling between UI, logic, and database made our heads nge-hang. The industry adopted the Three-Tier Architecture (Presentation, Application, Data) to enforce Separation of Concerns.

To help developers, Martin Fowler published Patterns of Enterprise Application Architecture (PoEAA). The crown jewel was the Domain Model pattern. Objects shouldn't be dumb data bags. Objects should encapsulate both state and behavior. A User object shouldn't just have getters and setters; it needs a lockAccount() method that enforces business rules internally.

To save and load these smart objects, developers turned to ORMs (Object-Relational Mapping). Hibernate or Entity Framework promised to seamlessly translate objects into relational database rows and back.

ORMs ran headfirst into the Object-Relational Impedance Mismatch. OOP is about graphs and encapsulation; databases are about flat tables. Mapping a rich, behavioral object to a table was a mbulet (convoluted) nightmare of lazy-loading issues and N+1 queries.

Developers took the easy way out: they stripped the behavior from objects to make the ORM's job easier. The Anemic Domain Model (ADM) anti-pattern was born. Objects became dumb again, and business logic was shoved back into "Use Case" services. The Three-Tier Architecture became hollow, serving as little more than a pass-through for the ORM and the ERD.

The Rails Revolution and the Triumph of ActiveRecord

Just as we were drowning in ORM configurations, Ruby on Rails arrived in 2004 and conquered the web. Rails popularized the ActiveRecord pattern.

ActiveRecord threw architectural purity out the window for unprecedented developer velocity. It flattened the Three-Tier Architecture and embraced the Impedance Mismatch. Instead of fighting to map objects to tables, ActiveRecord fused them: your database table is your object.

Rails proved that for 80% of web apps (simple CRUD), ActiveRecord was a superpower. Features were built in days, not weeks. The pattern was copied into every major language (Laravel in PHP, JPA in Java, GORM in Go).

ActiveRecord was the ultimate enabler of the Anemic Domain Model. Because the object was tied to the table, putting complex business logic inside it created "Fat Models" (It also can create Fat Controllers or God Objects). Over years of rapid iteration, these monoliths inevitably devolved into a Big Ball of Mud—a tangled, tightly coupled mess where every controller talked to every table.

The Microservices Illusion and the Distributed Big Ball of Mud

As monolithic "Big Balls of Mud" became impossible to scale, the industry looked for an escape hatch. Enter Microservices. The promise was seductive: break the monolith into small, independent services.

Engineers eagerly carved up their monoliths without a principled guide. They didn't use DDD; they just grouped services by technical function or arbitrarily sliced the ERD.

Without Bounded Contexts, the services were still tightly coupled. They shared databases, made synchronous HTTP calls, and introduced distributed transactions. We traded a local Big Ball of Mud for a Distributed Big Ball of Mud. A single user request now required five network hops, introducing latency, cascading failures, and debugging nightmares. Uncle Bob’s SOLID principles couldn't save us. We needed a way to define true, decoupled business boundaries.

The Paradigm Shift: DDD, CQRS, and Event Sourcing

We finally realized the database is just an implementation detail, and network boundaries must align with business boundaries. Enter Domain-Driven Design (DDD). DDD emerged to rescue the rich Domain Model and provide the boundaries microservices desperately needed. It discarded the ERD-first approach, enforcing a Ubiquitous Language and Bounded Contexts.

But DDD had a fundamental tension: we used the exact same data model to enforce complex business rules (writes) and render complex UI dashboards (reads). This mismatch caused the "Fat Models." Enter CQRS (Command Query Responsibility Segregation), introduced by Greg Young. A radical split:

  • Write Model (Commands): Handles complex DDD Aggregates, enforcing business invariants and transactions. Optimized for consistency, completely ignoring how the UI wants to see the data.
  • Read Model (Queries): Stripped of all business logic. Optimized purely for fetching data fast, using flat tables or NoSQL documents that perfectly match the UI.

Alongside CQRS came Event Sourcing.

DDD aligned code with business boundaries. CQRS solved the read/write tension permanently. Event Sourcing made the event the source of truth, providing 100% auditability—a lifesaver for Fintech. By focusing on events rather than state, we bypassed the ORM Impedance Mismatch entirely.

The cognitive load of DDD, CQRS, and Event Sourcing is massive. You don't need CQRS for a simple admin panel; CRUD and ActiveRecord are still 10x faster to build. Event Sourcing introduces eventual consistency, complex schema versioning, and hard-to-debug projections. It is a heavy weapon that will make a team mbledos (explode) if misused.

The Missing Link: Event Storming and the Rise of AI

To bridge the gap between domain experts and developers, Alberto Brandolini created Event Storming. Instead of writing static Use Case documents or staring at ERDs, we look at domain events ("Order Placed", "Payment Verified"). Business and engineers finally speak the same language to discover Bounded Contexts in real-time.

And what I've felt strongly lately: the rise of AI. Wooo, imagine this... Building DDD used to take a lot of works, writing boilerplate Aggregates and Event Sourcing until you went bald. Now? AI makes implementing domain models and DDD quick. What used to take months of thinking about the base framework, AI now helps scaffold quickly. We can focus on business logic without typing repetitive base code. DDD, which used to be heavy, is now much easier to execute.

TaroDomain Framework

TaroDomain is a DDD + Event Sourcing framework designed to be polyglot and cloud-agnostic. It provides a clean domain model architecture that allows you to persist aggregates using either State-Based or Event Sourcing mechanisms, depending on the bounded context's needs.

The Design Principles of TaroDomain:

  • Persistence Agnostic: Define your Aggregates and Domain Events once. You can change which storage to use easily via interfaces.
  • Polyglots: The core architecture is designed to be implemented in any language. We are starting with TypeScript, but the design patterns allow for easy porting to Elixir, C#, or Go.
  • Platform Agnostic: The initial implementation is heavily optimized for Cloudflare services and using TypeScript because CloudFlare is the cheapest platform compare to other cloud platforms. Later on any one can implement the adapters for other cloud platforms.

In the next part 2 I will wrote about the framework design and implementation.

Top comments (0)