generate-domain-model · git:20260822.b0cab65 · 2026-08-22 · sha256 1e6544eb60f9291d

generate-domain-model git:20260822.b0cab65A

Immutable. This exact content is served forever at /api/v1/blob/1e6544eb60f9291d.

---
name: generate-domain-model
description: "Generate a complete Mendix domain model in MDL — entities, attributes, associations, enumerations — and validate it. Use when asked for a domain model for a business area (e-commerce, HR, CRM, …) rather than a single entity."
---

# Creating Mendix Domain Model MDL Scripts

Use this skill to generate Mendix domain model scripts in MDL (Mendix Definition Language) format and validate them with the linter.

## Reference files

`SKILL.md` covers the process and the decisions — what to model, in what order,
and how to check it. The lookup material is next door:

- [`reference/syntax.md`](reference/syntax.md) — the complete syntax for entities,
  attributes, associations and enumerations, every attribute type, plus reserved
  keywords and entity positioning. **Check a spelling here rather than guessing**;
  a reserved word used as an attribute name fails in Studio Pro, not in the parser.
- [`reference/patterns.md`](reference/patterns.md) — the recurring domain shapes
  (header/detail, categorisation, audit, soft delete, many-to-many with payload)
  and a full worked e-commerce model.

## When to Use This Skill

- User asks to create a domain model for a specific use case
- User wants to generate entities, associations, and enumerations
- User requests a complete e-commerce, HR, CRM, or other business domain model
- User needs validation of generated MDL scripts

## Modelling for the client: widgets bind members, not expressions

A page widget binds an **attribute or association path** — never an expression. There
is no `substring(attr, i, 1)` or computed binding in a widget. So a value that must be
rendered per-part (each character of a code, each cell of a grid, each of N pencil
marks) has to be stored as **separate attributes**, not packed into one string and
indexed client-side. Model the wide form when the UI needs to address the parts
individually (e.g. `N1`…`N9` booleans rather than a packed `Notes` string); reach for
a computed/derived value only where a microflow or view-entity OQL produces it into a
real attribute. (Same reason the bucket-class idiom exists — see
`migrate-design-prototype`.)

## Documentation Best Practices

### Entity Documentation

```sql
/**
 * Brief one-line summary
 *
 * Detailed multi-line description explaining:
 * - What the entity represents
 * - Key business rules
 * - Relationships to other entities
 *
 * @since 1.0.0
 * @see Module.RelatedEntity
 */
```

### Attribute Documentation

```sql
/** Brief description of what this attribute stores */
attributename: type,
```

### Association Documentation

```sql
/**
 * Relationship description
 *
 * Explains the business meaning of this association.
 *
 * @since 1.0.0
 */
```

## Step-by-Step Process

### 1. Analyze Requirements

When user requests a domain model:
1. Identify core entities (nouns)
2. Identify enumerations (status, types, categories)
3. Identify relationships (associations)
4. Identify attributes for each entity
5. Check for reserved keyword conflicts

### 2. Generate MDL Script

Create script with this structure:
```sql
-- ============================================================================
-- Domain Model Name
-- ============================================================================
-- Description of the domain
-- ============================================================================

-- MARK: ENUMERATIONS

create enumeration Module.Enum1 (...);
create enumeration Module.Enum2 (...);

-- MARK: CORE ENTITIES

-- MARK: - Entity Group 1

create persistent entity Module.Entity1 (...);
create persistent entity Module.Entity2 (...);

-- MARK: - Entity Group 2

create persistent entity Module.Entity3 (...);

-- MARK: VIEW ENTITIES

create view entity Module.View1 as ...;

-- MARK: ASSOCIATIONS

-- MARK: - Entity Group 1 Associations

create association Module.Assoc1 ...;
create association Module.Assoc2 ...;
```

### 3. Validate with Linter

Run the linter to check for issues:

```bash
# Standalone test
node dist/test-linter-standalone.js

# or create a custom test file
```

The linter will detect:
- ✅ Reserved keywords (CE7247)
- ✅ Duplicate names (CE0065)
- ✅ OQL syntax errors (CE0174)

### 4. Review and Fix Issues

**Common Issues**:

1. **Reserved Keyword Error**:
   ```
   error: Reserved keyword 'CreatedDate' used as attribute name
   💡 rename to 'CreationDate'
   ```
   Fix: Rename to suggested alternative

2. **Duplicate Name Error**:
   ```
   error: Duplicate name 'Status' in module 'Shop'
   💡 rename one of the enumeration, entity to avoid conflict
   ```
   Fix: Rename entity to `OrderStatus` or similar

3. **OQL Syntax Error**:
   ```
   error: ORDER by requires limit or offset
   💡 add limit clause to query
   ```
   Fix: Add `limit 100` to view entity query

### 5. Generate Complete Script

Ensure:
- ✅ All entities have JavaDoc documentation
- ✅ All attributes have inline comments
- ✅ All associations have descriptions
- ✅ Position annotations for all entities
- ✅ No reserved keywords
- ✅ No duplicate names
- ✅ Valid OQL queries

## Testing the Script

1. **Save to file**: Save as `examples/my-domain-model.mdl`

2. **Run standalone linter**:
   ```bash
   node dist/test-linter-standalone.js
   ```

3. **Execute in REPL**:
   ```sql
   mendix> connect to FILESYSTEM 'path/to/project.mpr';
   mendix> execute script 'examples/my-domain-model.mdl';
   ```

4. **Check Studio Pro**: Open project and verify entities appear correctly

## Checklist

Before finalizing an MDL script:

- [ ] All entities have JavaDoc documentation
- [ ] All attributes have inline comments
- [ ] All associations have descriptions
- [ ] Position annotations on all entities
- [ ] MARK comments for files 300+ lines (at least 3 sections)
- [ ] All identifiers quoted with double quotes
- [ ] No duplicate names (run linter)
- [ ] Valid OQL queries in view entities (run linter)
- [ ] Consistent naming conventions (PascalCase)
- [ ] Appropriate data types and lengths
- [ ] Required fields marked with NOT NULL
- [ ] Validation error messages added for NOT NULL and UNIQUE constraints
- [ ] IDs marked with NOT NULL UNIQUE
- [ ] Email/unique fields marked with UNIQUE

## References

- **Reserved Keywords**: `packages/mendix-repl/docs/reference/reserved-keywords.md`
- **Linter Proposal**: `packages/mendix-repl/docs/proposals/mdl-linter-proposal.md`
- **Example Scripts**:
  - `packages/mendix-repl/examples/shop-domain-model.mdl`
  - `packages/mendix-repl/examples/pet-store-domain-model.mdl`
- **Linter Test**: `packages/mendix-repl/src/test-linter-standalone.ts`

## Tips for AI Assistants

1. **Always quote all identifiers** with double quotes to avoid MDL parser keyword conflicts — but note quoting does **not** exempt platform-reserved member names (`Type`, `CreatedDate`, `ChangedDate`, `Owner`, `ChangedBy`, `ID`, …); rename those
2. **Use descriptive names** (ServiceType, CustomerOrder)
3. **Run linter** on generated scripts before presenting to user
4. **Fix all errors** reported by linter before finalizing
5. **Follow examples** in shop-domain-model.mdl and pet-store-domain-model.mdl
6. **Document thoroughly** - Studio Pro users benefit from good documentation
7. **Position thoughtfully** - Related entities should be visually grouped
8. **Test incrementally** - Generate in sections and validate each part