What is the difference between TDD (Test-Driven Development) vs DDD (Domain-Driven Design) vs DDD (Data-Driven Development)?
Answer
Overview
These three methodologies address different aspects of software development. They are not mutually exclusive — many teams combine them for robust applications.
- TDD — Focuses on how you write code (test-first approach)
- Domain-Driven Design — Focuses on what you model (business domain)
- Data-Driven Development — Focuses on what drives decisions (data and analytics)
TDD (Test-Driven Development)
TDD follows a strict Red-Green-Refactor cycle: write a failing test first, then write minimal code to pass it, then refactor.
dart// Step 1: RED — Write a failing test test('should calculate total price with tax', () { final cart = ShoppingCart(); cart.addItem(Item(name: 'Widget', price: 100)); expect(cart.totalWithTax(taxRate: 0.1), equals(110.0)); }); // Step 2: GREEN — Write minimal code to pass class ShoppingCart { final List<Item> _items = []; void addItem(Item item) => _items.add(item); double totalWithTax({required double taxRate}) { final subtotal = _items.fold(0.0, (sum, item) => sum + item.price); return subtotal * (1 + taxRate); } } // Step 3: REFACTOR — Improve without breaking tests
DDD — Domain-Driven Design
Domain-Driven Design structures code around the business domain using ubiquitous language shared between developers and domain experts. Key building blocks include Entities, Value Objects, Aggregates, Repositories, and Services.
dart// Domain Layer — Pure business logic (no Flutter/framework imports) // Entity — Has identity class Order { final String id; final Customer customer; final List<OrderItem> items; OrderStatus _status = OrderStatus.pending; Order({required this.id, required this.customer, required this.items}); OrderStatus get status => _status; double get totalAmount => items.fold(0.0, (sum, item) => sum + item.subtotal); void confirm() { if (items.isEmpty) throw DomainException('Cannot confirm empty order'); _status = OrderStatus.confirmed; } } // Value Object — No identity, compared by value class Money { final double amount; final String currency; const Money({required this.amount, this.currency = 'USD'}); Money operator +(Money other) { assert(currency == other.currency); return Money(amount: amount + other.amount, currency: currency); } bool operator ==(Object other) => other is Money && amount == other.amount && currency == other.currency; } // Repository — Abstracts data access abstract class OrderRepository { Future<Order?> findById(String id); Future<void> save(Order order); Future<List<Order>> findByCustomer(String customerId); }
DDD — Data-Driven Development
Data-Driven Development uses real user data, analytics, and metrics to guide development decisions — from feature prioritization to UI design.
dart// Data-Driven approach: A/B testing to decide UI class FeatureFlagService { final AnalyticsService _analytics; FeatureFlagService(this._analytics); Future<bool> shouldShowNewCheckout(String userId) async { // Decision based on data, not intuition final variant = await _analytics.getVariant( experiment: 'checkout_redesign', userId: userId, ); return variant == 'new_checkout'; } } // Track metrics to validate decisions class CheckoutAnalytics { void trackConversion({ required String variant, required double amount, required Duration timeToComplete, }) { analytics.logEvent('checkout_complete', { 'variant': variant, 'amount': amount, 'duration_seconds': timeToComplete.inSeconds, }); } }
Key Differences
| Aspect | TDD | Domain-Driven Design | Data-Driven Development |
|---|---|---|---|
| Focus | Code correctness | Business domain modeling | Data-informed decisions |
| Drives | Implementation | Architecture & design | Feature prioritization |
| Key Artifact | Test suite | Domain model | Analytics dashboard |
| Methodology | Red-Green-Refactor | Ubiquitous Language, Bounded Contexts | A/B testing, KPIs |
| When to Use | All projects (especially critical logic) | Complex business domains | Products with real users |
| Team Needs | Developers | Developers + Domain Experts | Developers + Data Analysts |
| Scale | Function/class level | System/architecture level | Product/feature level |
When to Use Each
- TDD — Use for any project where code reliability matters. Essential for financial calculations, authentication logic, and business rules
- Domain-Driven Design — Use for complex enterprise apps with rich business logic (e-commerce, banking, healthcare). Overkill for simple CRUD apps
- Data-Driven Development — Use for consumer-facing products where user behavior data is available. Ideal for optimizing onboarding flows, feature adoption, and conversion rates
Combining All Three
The best teams combine these approaches:
text1. Use DDD to model the business domain correctly 2. Use TDD to ensure domain logic is bug-free 3. Use Data-Driven Development to prioritize which features to build and validate their impact
Key Insight: TDD answers "Is my code correct?", Domain-Driven Design answers "Am I building the right model?", and Data-Driven Development answers "Am I building the right features?".