Jaco van der Laan

A Minimal MDDE Project Structure — How to organize metadata, rules, SQL generation, and validation…

10/02/2026  ·  Data Engineering  ·  Metadata

metadata sql
‹ ›

Why structure matters more than tooling


A Minimal MDDE Project Structure — How to organize metadata, rules, SQL generation, and validation in practice.

Why structure matters more than tooling

Most architectural approaches fail not because the ideas are wrong, but because there is no clear place to put them.

  • Guidelines float in documents.
  • Rules live in ad-hoc scripts.
  • SQL sits wherever it happened to be created.

MDDE takes a different stance:

Architecture should be visible in the project structure itself.

This article describes a minimal, opinionated MDDE project layout that has proven practical in real work — without over-engineering.


Design goals

The structure aims to be:

  • Explicit — intent is easy to find
  • Composable — pieces evolve independently
  • Enforceable — rules live next to code
  • Tool-agnostic — no framework lock-in

You should be able to adopt this incrementally.


The minimal MDDE layout

mdde-project/  
│  
├── metadata/  
│   ├── entities.yaml  
│   ├── attributes.yaml  
│   ├── relationships.yaml  
│   └── semantics.yaml  
│  
├── rules/  
│   ├── sql/  
│   │   ├── no_select_star.py  
│   │   ├── explicit_joins.py  
│   │   └── deterministic_filters.py  
│   └── README.md  
│  
├── generation/  
│   ├── templates/  
│   │   ├── select.sql.j2  
│   │   └── join.sql.j2  
│   └── generate_sql.py  
│  
├── validation/  
│   ├── regression/  
│   │   ├── compare_results.py  
│   │   └── compare_structure.py  
│   └── validate.py  
│  
├── adr/  
│   ├── ADR-001-parse-sql.md  
│   ├── ADR-002-regression-testing.md  
│   └── ADR-003-deterministic-generation.md  
│  
├── diagnostics/  
│   └── runs/  
│       └── 2025-02-01.json  
│  
└── README.md

Let’s walk through this piece by piece.


Metadata: the source of intent

metadata/

This is where architectural intent lives.

Not SQL.
Not rules.
Not code.

Metadata captures:

  • What entities exist
  • How they relate
  • What semantics apply

Example (entities.yaml):

sales_summary:  
  grain: daily  
  source: sales_table

If something is not here, it is not part of the architecture.


Rules: executable guidelines

rules/

Rules implement architectural constraints.

Each rule:

  • Enforces one guideline
  • Has a clear scope
  • Produces observable output

Example:

# rules/sql/no_select_star.py

class NoSelectStarRule:  
    def validate(self, ast):  
        if ast.select_clause.contains_star():  
            return "SELECT * is not allowed"

Rules never modify behavior silently.
They detect, explain, and enforce.


Generation: intent → SQL

generation/

SQL generation is treated as a rendering step.

Templates are:

  • Deterministic
  • Explicit
  • Reviewable

    generate_sql.py

    sql = generate_sql(
    entity=”sales_summary”,
    engine=”postgres”
    )

Generated SQL is an output artifact , not a design surface.


Validation: comparison, not confidence

validation/

Validation answers:

  • Is this behavior equivalent?
  • If not, where does it differ?

    compare_structure(old_sql, new_sql)
    compare_results(old_results, new_results)

Validation lives alongside generation — not after deployment.


ADRs: decisions with a home

adr/

Every ADR:

  • Has a stable filename
  • Is referenced by rules or generators
  • Explains why , not how

This avoids architectural archaeology.


Diagnostics: architecture in motion

diagnostics/

Every run emits:

  • Applied rules
  • Violations
  • Generated SQL hashes
  • Validation outcomes

    {
    “entity”: “sales_summary”,
    “rules_passed”: 12,
    “rules_failed”: 0,
    “sql_hash”: “9a7f…”
    }

Architecture becomes observable, not assumed.


Why this structure works

This layout enforces separation of concerns:

No single folder does everything.
No single artifact carries implicit meaning.


Start small

You don’t need all of this on day one.

You can start with:

  • metadata/
  • rules/
  • adr/

And add generation and validation as pressure increases.

The structure scales because intent scales before code.


What’s next

In the next articles, I’ll cover:

  • Rule design patterns (what makes a good rule)
  • How rules integrate with SQL generation
  • CI pipelines for MDDE-style validation
  • How to evolve metadata without breaking contracts

All continuing the same theme: architecture that is explicit, executable, and durable under change.