Data Modeling and why DDPs shouldn't be built like any other software

Mar 3, 2025

In the ever-evolving landscape of software development, Deep Domain Problems (DDPs) stand apart from conventional software challenges. While standard applications can often be built using established frameworks and patterns, DDPs demand a fundamentally different approach to data modeling and architecture.

The Unique Nature of DDPs

As outlined in our introduction to Deep Domain Problems, DDPs exist at the intersection of specialized domain knowledge, technical implementation, and organizational complexity. These problems are characterized by:

  1. Specialized domain knowledge acquired over decades of experience
  2. Tacit expertise that experts struggle to articulate clearly
  3. Cross-system and cross-stakeholder complexity
  4. Resistance to simple automation

These characteristics directly impact how we should approach data modeling for DDP solutions.

Traditional Data Modeling vs. DDP Data Modeling

Traditional Approach

In conventional software development, data modeling typically follows a predictable path:

  1. Gather requirements from stakeholders
  2. Create entity-relationship diagrams
  3. Define data schemas
  4. Implement models in code
  5. Iterate based on feedback

This process assumes that:

  • Requirements can be fully captured upfront
  • Entities and relationships are relatively stable
  • The problem space is well-understood
  • Technical constraints take precedence over domain nuances

The DDP Approach

When tackling Deep Domain Problems, the data modeling process must be fundamentally different:

  1. Domain-First Modeling: Begin with the domain concepts rather than technical entities
  2. Ubiquitous Language Integration: Embed the precise terminology of the domain
  3. Context-Aware Boundaries: Recognize where concepts shift meaning across contexts
  4. Tacit Knowledge Capture: Design models that reflect expert intuition and judgment
  5. Evolutionary Architecture: Build for change and discovery rather than stability

The Foundations of DDP Data Modeling

1. Ubiquitous Language as the Cornerstone

As explored in Ubiquitous Language: The Foundation for Solving Deep Domain Problems, establishing a shared vocabulary between domain experts and developers is not optional but essential:

"When tackling complex systems, one of the most overlooked yet critical challenges is communication. Domain experts and technical teams often speak different languages, leading to misunderstandings, rework, and failed projects."

Your data model must directly reflect this ubiquitous language. Consider our lending example from Deep Domain Problems in Lending:

// Standard approach (generic terms)
interface UserRequest {
  userID: string;
  requestAmount: number;
  financialData: object;
  status: string;
}

// DDP approach (domain language)
interface LoanApplication {
  applicantID: string;
  requestedLoanAmount: Money;
  creditScore: number;
  debtToIncomeRatio: Percentage;
  applicationStatus: ApplicationStatus;
}

The second model captures domain concepts explicitly, making it intelligible to both domain experts and developers.

2. Bounded Contexts and Model Fragmentation

Complex domains naturally fragment into sub-domains with their own conceptual models. As noted in How to Break Down a Deep Domain Problem:

"Define bounded contexts... Map where concepts shift (e.g., 'order' in sales vs. shipping). Trace data handoffs between contexts. Specify translation rules at boundaries."

Your data model should respect these boundaries rather than forcing a monolithic representation. For example, in our supply chain optimization case study, a "Route" means something different in Planning (a proposed path) versus Execution (real-time conditions).

Rather than creating a single Route model, effective DDP modeling creates distinct models with explicit translations between contexts:

// Planning context
interface PlannedRoute {
  routeID: string;
  plannedPath: GeoPath;
  estimatedDuration: Duration;
  weatherConsiderations: WeatherData;
}

// Execution context
interface ExecutionRoute {
  routeID: string;
  currentPath: GeoPath;
  actualProgress: RouteProgress;
  realTimeConditions: EnvironmentalConditions;
}

// Translation mapping between contexts
interface ContextMapper {
  translateToExecution(plannedRoute: PlannedRoute): ExecutionRoute;
  updatePlanFromExecution(executionData: ExecutionRoute): PlannedRoute;
}

3. Capturing Decision Models and Expert Judgment

Standard data models rarely capture decision-making processes. Yet, for DDPs, these decisions embody the critical domain expertise. As illustrated in our lending case study's decision tree:

START: Loan Application
├── Credit Score  650?
   ├── Yes
      ├── Debt-to-Income Ratio  36%?
...

This decision logic must be explicitly modeled in your data structure, not buried in application code:

interface DecisionRule {
  condition: (context: EvaluationContext) => boolean;
  priority: number;
  outcome: Decision | null;
  nextRules: DecisionRule[] | null;
}

class LoanDecisionEngine {
  private rootRule: DecisionRule;

  evaluate(application: LoanApplication): Decision {
    const context = this.buildContext(application);
    return this.evaluateRules(this.rootRule, context);
  }
}

4. Value Objects Over Primitives

DDPs often involve domain concepts with intrinsic validation rules and behaviors. For example, in forex risk management from our WiredUp example, currency exchange rates are not merely numbers:

// Poor modeling (primitives)
const exchangeRate = 1.23;
const amount = 1000;
const convertedAmount = amount * exchangeRate;

// Rich domain modeling (value objects)
class CurrencyPair {
  constructor(private base: Currency, private quote: Currency) {
    // Validation logic ensuring a valid currency pair
  }

  // Methods for fetching current rates, historical volatility, etc.
}

class ExchangeRate {
  constructor(
    private pair: CurrencyPair,
    private rate: number,
    private timestamp: Date
  ) {
    // Validation logic
  }

  convert(amount: Money): Money {
    // Conversion with precision handling, rounding rules, etc.
  }
}

This rich modeling captures not just data but behavior and business rules.

The Technical Architecture for DDP Data Models

Beyond the conceptual aspects of data modeling, the technical implementation for DDPs requires specific architectural patterns:

1. Flexible Deployment Options

As detailed in Software Packaging: The Game Changer for Deep Domain Problem Solutions, your data model must accommodate diverse deployment environments:

"The way you package your solution can be the difference between success and failure in the enterprise market. Despite having the right domain expertise and technical capabilities, many solutions falter because they haven't considered the deployment requirements of their target customers."

This flexibility extends to your data model, which should support:

  • Multiple persistence mechanisms (SQL, NoSQL, hybrid)
  • Varied deployment contexts (cloud, on-premise, hybrid)
  • Different security and compliance requirements

2. Event-Sourcing for Complex Domains

For many DDPs, the journey and history of state changes matter as much as the current state. As illustrated in our Technical Patterns for Solving Deep Domain Problems:

interface DomainEvent {
  readonly type: string;
  readonly timestamp: Date;
  readonly aggregateId: string;
  readonly data: unknown;
}

class RouteAggregate {
  private state: Route;
  private events: DomainEvent[] = [];

  apply(event: DomainEvent): void {
    // Event handling logic
    this.events.push(event);
  }
}

This event-sourcing approach captures the rich history and decision points that characterize complex domains.

3. Discovery-Oriented Architecture

As noted in Discovery Coding in Deep Domain Problems, DDP solutions often emerge through exploration rather than upfront specification:

"Discovery Coding... is an approach where programmers explore solutions through code rather than extensive upfront planning. This methodology particularly resonates with DDPs [because] understanding emerges through hands-on engagement with the problem space."

Your data model should support this discovery process with:

  • Extensible schemas that can evolve as understanding deepens
  • Clear separation between core domain models and supporting elements
  • Mechanisms to capture and integrate new discoveries

Implementation Strategies

The Two-Phase Approach

Following Betalectic's methodology mentioned in The Evolving Role of Programmers in Solving Deep Domain Problems:

"I tell my team to solve problems twice: first, to make it work, and then to make it work well. When you're still figuring out the fundamental approach to a domain problem, creating an elegant solution immediately is nearly impossible."

For data modeling, this translates to:

Phase 1: Domain Discovery

  • Create initial models based on domain expert input
  • Prioritize domain fidelity over technical elegance
  • Use exploratory coding to test model validity
  • Document emerging patterns and exceptions

Phase 2: Technical Refinement

  • Refactor toward more elegant, maintainable models
  • Optimize performance for critical operations
  • Enhance flexibility for deployment scenarios
  • Strengthen validation and consistency rules

Collaboration Techniques

Effective DDP data modeling requires deep collaboration between domain experts and technical teams:

  1. Collaborative Modeling Sessions: Regular workshops where domain experts and developers co-create models using tools like whiteboards, sticky notes, and visualization software.

  2. Model Storming: Short, focused sessions to model specific domain aspects or scenarios, particularly useful for capturing tacit knowledge.

  3. Example Mapping: Working through concrete examples to validate model completeness and accuracy.

  4. Decision Record Keeping: Documenting modeling decisions and their rationale to preserve knowledge and context.

Common Pitfalls to Avoid

1. Premature Technical Optimization

Focusing on database efficiency or query performance before fully understanding the domain model can lead to models that poorly represent the domain.

2. Ignoring Context Boundaries

Forcing consistent terminology across different contexts leads to models that satisfy no one and confuse everyone.

3. Over-Reliance on Generic Patterns

While design patterns are valuable, applying them without domain justification creates artificial complexity.

4. Data-Only Thinking

Modeling just the data without capturing behaviors and rules misses the essence of the domain.

Conclusion

Data modeling for Deep Domain Problems requires a fundamentally different approach than conventional software development. By prioritizing domain fidelity, embracing complexity, and building for discovery and evolution, we create models that effectively capture both the explicit and tacit knowledge that defines complex domains.

As emphasized throughout our DDP content, the most valuable opportunities come from addressing complex challenges with solutions built on deep domain understanding. Data modeling is where this understanding first takes concrete form, laying the foundation for truly transformative solutions.

The next time you approach a Deep Domain Problem, resist the urge to model it like any other software project. Instead, invest in the rich, context-aware modeling that these complex challenges demand, and you'll build solutions that not only work technically but truly solve the deep domain problems that matter.