Question #451HardArchitectureImportant

What is the difference between TDD (Test-Driven Development) vs DDD (Domain-Driven Design) vs DDD (Data-Driven Development)?

#tdd#ddd#domain-driven-design#data-driven#testing#architecture

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

AspectTDDDomain-Driven DesignData-Driven Development
FocusCode correctnessBusiness domain modelingData-informed decisions
DrivesImplementationArchitecture & designFeature prioritization
Key ArtifactTest suiteDomain modelAnalytics dashboard
MethodologyRed-Green-RefactorUbiquitous Language, Bounded ContextsA/B testing, KPIs
When to UseAll projects (especially critical logic)Complex business domainsProducts with real users
Team NeedsDevelopersDevelopers + Domain ExpertsDevelopers + Data Analysts
ScaleFunction/class levelSystem/architecture levelProduct/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:

text
1. 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?".