Home Doh Ref
Dohballs
  • 📁 doh_modules
    • 📦 dataforge
    • 📦 express
    • 📁 sso
    • 📁 user

Core Architecture Overview

Doh's core insight: all software operates through namespace resolution, and incompatible namespace systems can be made to work together through controlled composition rules.

Multiple inheritance is just one namespace incompatibility problem. The diamond problem revealed why naive combination fails—but also showed us the pattern for solving namespace conflicts in general.

The Diamond Problem

The classic example that revealed why multiple inheritance is genuinely hard.

    Animal
     ↙ ↘
  Bird   Horse
     ↘ ↙
    Pegasus

The Real Problem

move() → "flying"
move() → "galloping"

// Which one gets inherited?
pegasus.move() → ???

Just running both methods isn't enough. What's the return value? The execution order? How do they share state?

Why "Just Combine Them" Fails

Ordering Should validation run before or after logging? Base before derived? Context matters.

Return Values One method returns altitude, another returns speed. Do you pick one? Average them? Create a tuple?

Hooking Need setup before methods run? Cleanup after? Simple stacking can't handle lifecycle hooks.

The Industry Response

Language Chaos Every language invented different rules. C++ uses virtual inheritance. Python has MRO linearization. Scala has trait linearization.

The complexity exploded. Developers couldn't predict behavior. Debugging became a nightmare.

The Great Retreat Java banned it. C# followed. JavaScript never had it. Go rejected it from the start.

The mantra became universal: "Composition over inheritance."

Diamond Problem Core Challenges

The diamond problem has two core challenges:

  1. Inheritance Order - Which parent comes first? How do you traverse complex hierarchies predictably?
  2. Conflict Resolution - When multiple parents define the same property, which wins? How do you combine them?

Methods make this exponentially harder—functions can't just "merge" like objects or arrays can.

The Solution: Namespace Composition Framework

The diamond problem taught us the pattern for solving any namespace conflict: you need both ordering and melding rules. This same pattern applies beyond inheritance to all namespace incompatibilities.

1. Resolution Order Algorithm

Any namespace conflict needs ordering. We use topological sort to flatten complex hierarchies into predictable ordered lists.

    Animal
     ↙ ↘
  Bird   Horse
     ↘ ↙
    Pegasus

Resolves to: [Animal, Bird, Horse, Pegasus]

Four phases make this more powerful than this simple example shows: source identification, recursive expansion, deterministic flattening, and conditional evaluation. But the gist is: flatten the graph.

2. Melded Object Composition (MOC)

MOC is our type AND merge system in one—when multiple patterns define the same property, explicit rules determine how they combine.

Pattern('Vehicle', {
    moc: {
        move: 'Chain',    // Chain methods together
        speed: 'Method'   // Blend returns onto this
    },
    move() { return 'base movement'; },
    speed() { return { base: 10 }; }
});

Pattern('Bird', 'Vehicle', {
    move(prev) { return prev + ' → flying'; },
    speed() { return { air: 25 }; }
});

Pattern('Horse', 'Vehicle', {
    move(prev) { return prev + ' → galloping'; },
    speed() { return { ground: 35 }; }
});

Pattern('Pegasus', ['Bird', 'Horse'], {
    move(prev) { return prev + ' → majestically'; }
});

const pegasus = New('Pegasus');
pegasus.move();  // "base movement → flying → galloping → majestically"
pegasus.speed;   // { base: 10, air: 25, ground: 35 }

Results:

  • Ordering handled by Resolution Order algorithm
  • Return Values melded according to MOC rules
  • Hooking built-in with pre/post method lifecycle

The same MOC rules work for arrays, objects, and complex melding scenarios.

MOC Rule Types (Some Commons):

  • 'Method' - Returns blend onto this using MOC rules
  • 'Chain' - Functions pipe return values to next function
  • 'Deep' - Objects merge recursively
  • 'Array' - Arrays merge uniquely, removing duplicates

Building Up, Not Mixing In

MOC definitions themselves are inherited. Define behavior rules once, they cascade through your entire inheritance chain.

// Traditional Mixins
const LoggerMixin = {
    log() { /* self-contained */ }
};
const ValidatorMixin = {
    validate() { /* separate */ }
};

// Doh Patterns
Pattern('Base', {
    moc: { process: 'Chain' }
});
// All descendants inherit the rule
Pattern('Child', 'Base', {
    process(data) { /* adds to chain */ }
});

Inheritance Becomes Composition

// Not "IS-A" or "HAS-A" but "DOES-ALL"
const component = New(['Draggable', 'Resizable', 'Clickable']);

// All behaviors are active and collaborative
// No conflicts, no ambiguity, no choosing sides

Maybe multiple inheritance can work?

Testing the Theory

We had a solution for multiple inheritance. But where were its limits? We kept looking for places where these core principles would break down.

Long before ESM existed, we had script tags executing immediately on parse. No dependency resolution, no guaranteed order. Testing our pattern system was impossible when you couldn't control execution flow.

Simple extension: jQuery's $(document).ready() let you wait for DOM. Why not wait for other callbacks? Doh.Module(name, deps, callback) was born—dependency-aware document.ready().

The realization: Modules are just another namespace system.

The Evolution

Each piece unlocked the next logical problem. As web standards matured, we leveraged what we could—JSON, Node.js, ESM. But the core insight remained: the same ordering and melding principles worked everywhere.

→ Auto-Packager emerges We wanted to inject args into our callbacks by name, and move modules around from file to file without updating anything. AST parsing scanned source code to understand what each module needed, built dependency graphs, generated manifests.

→ DohPath emerges Moving modules around broke assets. We needed path resolution that worked regardless of where files lived. Context-relative paths (^/) let you reference files relative to project root, module context, or packages.

→ Pod system emerges As systems matured, modules needed configuration defaults. Not massive config files—sparse YAML that inherited and layered with deep melding and explicit removal operators (~~).

→ Express middleware layer When Node.js came, Express was perfect—we just needed it to work with our module system. Thin compatibility layer for middleware integration, file-based routing, pod-driven configuration. No rebuilding for the sake of it.

→ SPR (Scoped Parameter Resolution) Functions need parameters, but declaring them at every call site breaks modularity. Parameters resolve by name through scope chains, enabling true reuse without redeclaration.

And this only scratches the surface. The same principles that solved the diamond problem became the foundation for security systems, data pipelines, development tools, cloud orchestration.

The Namespace Composition Framework

What started as solving the diamond problem became a framework for making any incompatible namespace systems work together. After years of applying ordering + melding principles across every domain we encountered created this surprisingly comprehensive ecosystem.

Core Infrastructure

Load System & Auto-Packager: Universal dependency loading with build-time analysis

  • Cross-environment compatibility (Node, Browser, Bun)
  • Automatic NPM installation from import statements
  • Manifest generation for runtime optimization
  • Declarative load DSL with environment branching

DohPath: Universal path resolution across environments

  • Project-relative (/), context-relative (^/), and package (^:pkg/) paths
  • Unified API for Node, Browser, and virtual filesystems
  • Cross-platform compatibility with automatic normalization

Patterns & MOC: Multiple inheritance object composition

Modules & SPR: Dependency injection with parameter resolution

  • Scoped Parameter Resolution by name, not position
  • Environment branching with conditional loading
  • Async module initialization support
  • Auto-Installer for NPM dependency management

Pods: Sparse configuration with deep melding

  • YAML-based hierarchical configuration
  • Deep merge with array uniquing and key removal
  • Environment-specific overrides
  • Security by design with browser/server namespace separation

Application Systems

Express Router: Protocol-agnostic HTTP/WebSocket routing

  • Unified route handling for both HTTP and WebSocket protocols
  • File-based routing with automatic middleware integration
  • Pod-driven configuration

Cloud Manager: Multi-instance orchestration

  • Distributed Doh instance management
  • File synchronization and log aggregation

Data Pipeline: Cross-environment data processing

  • Declarative pipelines working identically across Node/Browser/Bun
  • Database abstraction with synchronous APIs

Security & Identity

User System: Authentication and session management

  • JWT authentication with multi-device sessions
  • Email verification and brute-force protection

Permission System: Context-aware authorization

  • Runtime object evaluation: Doh.permit(user, 'action:context', runtimeObject)
  • Role-based access with object-level security

Development Tools

Browser IDE: Web-based development environment

  • VS Code-compatible editor with live filesystem access
  • Terminal integration via server-side PTY processes

MCP Server: AI assistant integration

  • AST-powered code search and analysis
  • Browser automation via Playwright

Framework Validation

The namespace composition principles have proven universal across domains:

  • Security Systems: Permission-based authorization with runtime object evaluation
  • Data Pipelines: Cross-environment processing with declarative composition
  • Development Tools: AST-powered analysis and manifest generation
  • Cloud Orchestration: Multi-instance management with configuration melding
  • UI Components: Pattern-based composition with lifecycle management

Each system demonstrates the same pattern: when namespace systems are incompatible, ordering + melding rules provide the solution. The framework adapts to new namespace incompatibilities without breaking existing patterns, proving that this approach scales beyond any single problem domain.

Last updated: 10/22/2025