This document provides a comprehensive analysis of the original Mendix Model SDK (TypeScript) and the approach for creating a fully equivalent Go implementation.
- Overview
- Original SDK Architecture
- Current Go Implementation
- Gap Analysis
- Code Generation Strategy
- Maintenance Process
- Implementation Roadmap
The goal is to create a 100% functional equivalent of the Mendix Model SDK for local Mendix projects in Go. The original SDK consists of two main components:
| Component | Purpose | Go Equivalent |
|---|---|---|
| mendixmodelsdk | High-level OO API for model manipulation | modelsdk-go packages |
| mendixmodellib | Low-level format handling, metamodel definitions | mpr/ package + generated types |
- Original SDK: Cloud-first, connects to Mendix Team Server, real-time collaboration
- Go Implementation: Local-first, works directly with
.mprfiles, no cloud dependency
The high-level SDK providing:
- 52 metamodel domains with 820+ type definitions per version
- Delta-based change system for CRUD operations with undo/redo
- Cloud connectivity for Team Server (SVN/Git)
- Real-time synchronization via Server-Sent Events
- Working copy management with locking and collaboration
| Domain | Description | Types |
|---|---|---|
domainmodels |
Entities, attributes, associations | ~50 |
microflows |
Microflow activities and flows | 150+ |
pages |
UI widgets, layouts, snippets | 500+ |
workflows |
BPM workflow definitions | 200+ |
rest |
REST/OData services | ~40 |
webservices |
SOAP web services | ~30 |
security |
Access rules, user roles | ~25 |
navigation |
Menus and navigation | ~20 |
mappings |
Import/export mappings | ~30 |
expressions |
Expression language | ~100 |
| ... | 42 more domains | ... |
The low-level library providing:
- MPR file format handling (SQLite + BSON)
- Metamodel reflection data for 88 Mendix versions (6.0.0 - 11.6.0)
- Delta processing for model transformations
- GUID utilities for binary encoding
- Version compatibility checking
Located in libs/mendixmodellib/reflection-data/:
reflection-data/
├── 6.0.0-structures.json # Mendix 6.0.0 type definitions
├── 6.0.0-storageNames.json # storage name mappings
├── ...
├── 11.6.0-structures.json # Latest version
└── 11.6.0-storageNames.json
Each {version}-structures.json contains:
{
"DomainModels$entity": {
"qualifiedName": "DomainModels$entity",
"storageName": "DomainModels$entity",
"superTypeName": "DomainModels$MaybeRemotableElement",
"abstract": false,
"type": "ELEMENT",
"properties": {
"name": {
"name": "name",
"storageName": "Name",
"list": false,
"typeInfo": {
"type": "PRIMITIVE",
"primitiveType": "string"
}
},
"attributes": {
"name": "attributes",
"storageName": "attributes",
"list": true,
"typeInfo": {
"type": "ELEMENT",
"elementType": "DomainModels$attribute",
"kind": "PART"
}
}
// ... more properties
},
"defaultSettings": {
"name": "",
"documentation": ""
}
}
}modelsdk-go/
├── modelsdk.go # Public api entry points
├── model/ # Core types (ID, module, project, etc.)
├── domainmodel/ # entity, attribute, association types
├── microflows/ # microflow, nanoflow types
├── pages/ # page, layout, snippet types
├── mpr/ # MPR file reading/writing
│ ├── reader.go # read-only access
│ ├── writer.go # read-write access
│ ├── parser.go # BSON parsing
│ └── utils.go # UUID generation, etc.
└── examples/ # Usage examples
| Feature | Status | Notes |
|---|---|---|
| MPR v1/v2 reading | ✅ Complete | SQLite + BSON parsing |
| MPR v1/v2 writing | ✅ Complete | Transaction support |
| Module CRUD | ✅ Complete | |
| Entity CRUD | ✅ Complete | |
| Attribute types | ✅ Complete | 9 types |
| Association CRUD | ✅ Complete | |
| Microflow basic | Basic structure only | |
| Nanoflow CRUD | CREATE/DROP/DESCRIBE/SHOW/RENAME/MOVE, GRANT/REVOKE, diff, JavaScript action calls, association retrieve roundtrip | |
| Page basic | Basic structure only | |
| JSON export | ✅ Complete |
| Domain | TypeScript Lines | Go Lines | Coverage |
|---|---|---|---|
| domainmodels | 3,597 | ~478 | ~13% |
| microflows | 6,628 | ~200 | ~3% |
| pages | 34,062 | ~100 | <1% |
| workflows | 3,471 | 0 | 0% |
| rest | 2,453 | 0 | 0% |
| expressions | 7,591 | 0 | 0% |
48 of 52 domains are not implemented:
High Priority:
workflows- Workflow/BPM definitionsrest- REST service consumption/publishingwebservices- SOAP web servicessecurity- Access rules, module rolesnavigation- Navigation profiles, menusexpressions- Expression language types
Medium Priority:
mappings,importmappings,exportmappingsjsonstructures,xmlschemasjavaactions,javascriptactionscustomwidgetsreports,datasetsdocumenttemplates
Lower Priority:
kafka,queues,businesseventsappservicesmlmappingsdatabaseconnectorexceldataimporter- ... and more
Even in implemented domains, many subtypes are missing:
Microflows (150+ activities needed):
ActionActivity,LoopedActivityCreateObjectAction,ChangeObjectAction,DeleteActionRetrieveAction,AggregateListActionMicroflowCallAction,JavaActionCallActionShowMessageAction,ShowPageActionLogMessageAction,ValidationFeedbackAction- ... and 140+ more
Pages (500+ widgets needed):
- Container widgets:
dataview,listview,datagrid,TemplateGrid - Input widgets:
textbox,textarea,dropdown,datepicker - Button widgets:
actionbutton,linkbutton - Layout widgets:
container,groupbox,tabcontainer - ... and 480+ more
| Component | Description | Status |
|---|---|---|
| Metamodel reflection | Runtime type inspection | ❌ Not implemented |
| Version compatibility | Check type availability per version | ❌ Not implemented |
| Delta system | Change tracking with undo/redo | ❌ Not implemented |
| Property system | Type-safe property access | ❌ Not implemented |
| Qualified names | Path-based element resolution |
The mendixmodellib/reflection-data/ contains complete metamodel definitions in JSON format. We can generate Go code automatically from these definitions.
- Accuracy: Generated types match the official metamodel exactly
- Maintainability: New Mendix versions only require running the generator
- Completeness: All 820+ types per version can be generated
- Version support: Can generate version-specific types or union types
┌─────────────────────────────────────────────────────────────┐
│ Code Generator │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Parser │───▶│ transformer │───▶│ Emitter │ │
│ │ │ │ │ │ │ │
│ │ read json │ │ build type │ │ generate Go │ │
│ │ metamodel │ │ hierarchy │ │ source code │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ structures │ │ Go type │ │ .go files │ │
│ │ .json │ │ definitions │ │ per domain │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
// cmd/generate/main.go
package main
// Generator reads reflection data and produces Go types
type Generator struct {
version string // Target Mendix version
structures map[string]*structure
OutputDir string
}
// structure from reflection-data json
type structure struct {
QualifiedName string `json:"qualifiedName"`
StorageName string `json:"storageName"`
SuperTypeName string `json:"superTypeName,omitempty"`
Abstract bool `json:"abstract"`
type string `json:"type"` // ELEMENT, MODEL_UNIT, STRUCTURAL_UNIT
properties map[string]*Property `json:"properties"`
DefaultSettings map[string]any `json:"defaultSettings"`
}
// Property definition
type Property struct {
Name string `json:"name"`
StorageName string `json:"storageName"`
list bool `json:"list"`
Public bool `json:"public"`
TypeInfo *TypeInfo `json:"typeInfo"`
}
// TypeInfo for property types
type TypeInfo struct {
type string `json:"type"` // PRIMITIVE, ELEMENT, enumeration, UNIT
PrimitiveType string `json:"primitiveType,omitempty"`
ElementType string `json:"elementType,omitempty"`
Kind string `json:"kind,omitempty"` // PART, BY_ID_REFERENCE, BY_NAME_REFERENCE
}generated/
├── domainmodels/
│ ├── entity.go
│ ├── attribute.go
│ ├── association.go
│ └── types.go
├── microflows/
│ ├── microflow.go
│ ├── activities.go # 150+ activity types
│ ├── actions.go # action implementations
│ └── types.go
├── pages/
│ ├── page.go
│ ├── widgets.go # 500+ widget types
│ ├── layouts.go
│ └── types.go
├── workflows/
│ ├── workflow.go
│ ├── activities.go
│ └── types.go
└── ... (48 more domains)
| JSON Type | Go Type |
|---|---|
PRIMITIVE/string |
string |
PRIMITIVE/integer |
int64 |
PRIMITIVE/DOUBLE |
float64 |
PRIMITIVE/boolean |
bool |
PRIMITIVE/DATE_TIME |
time.Time |
PRIMITIVE/GUID |
model.ID |
PRIMITIVE/POINT |
model.Point |
PRIMITIVE/SIZE |
model.Size |
PRIMITIVE/COLOR |
model.Color |
PRIMITIVE/BLOB |
[]byte |
ELEMENT (single) |
*TypeName |
ELEMENT (list) |
[]*TypeName |
BY_ID_REFERENCE |
model.ID |
BY_NAME_REFERENCE |
model.QualifiedName |
enumeration |
Custom enum type |
Input (11.6.0-structures.json):
{
"DomainModels$entity": {
"qualifiedName": "DomainModels$entity",
"superTypeName": "DomainModels$MaybeRemotableElement",
"properties": {
"name": {
"storageName": "Name",
"typeInfo": { "type": "PRIMITIVE", "primitiveType": "string" }
},
"attributes": {
"storageName": "attributes",
"list": true,
"typeInfo": { "type": "ELEMENT", "elementType": "DomainModels$attribute", "kind": "PART" }
},
"generalization": {
"storageName": "generalization",
"typeInfo": { "type": "ELEMENT", "elementType": "DomainModels$GeneralizationBase", "kind": "PART" }
}
}
}
}Output (generated/domainmodels/entity.go):
// Code generated by modelsdk-generator. DO not EDIT.
// source: 11.6.0-structures.json
package domainmodels
import "github.com/mendixlabs/mxcli/model"
// entity represents a DomainModels$entity element.
type entity struct {
model.BaseElement
// Name is the entity name (storage: Name)
Name string `json:"name" bson:"Name"`
// attributes contains the entity's attributes (storage: Attributes)
attributes []*attribute `json:"attributes,omitempty" bson:"attributes"`
// generalization defines inheritance (storage: generalization)
generalization GeneralizationBase `json:"generalization,omitempty" bson:"generalization"`
}
// GetName returns the entity's name.
func (e *entity) GetName() string {
return e.Name
}
// Ensure entity implements the required interfaces.
var (
_ model.Element = (*entity)(nil)
_ model.NamedElement = (*entity)(nil)
)When a new Mendix version is released:
┌─────────────────────────────────────────────────────────────┐
│ version update Process │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Obtain new reflection data │
│ └─▶ get {version}-structures.json from mendixmodellib │
│ │
│ 2. run generator │
│ └─▶ go run cmd/generate/main.go -version=11.7.0 │
│ │
│ 3. Review changes │
│ └─▶ git diff generated/ │
│ │
│ 4. update parser mappings │
│ └─▶ add new type handlers if needed │
│ │
│ 5. run tests │
│ └─▶ go test ./... │
│ │
│ 6. update version constants │
│ └─▶ add to supported versions list │
│ │
└─────────────────────────────────────────────────────────────┘
# Option A: from npm package
npm pack mendixmodellib@latest
tar -xzf mendixmodellib-*.tgz
cp package/reflection-data/*.json libs/mendixmodellib/reflection-data/
# Option B: from existing installation
cp ~/Projects/mcpmxsdk/node_modules/mendixmodellib/reflection-data/*.json \
libs/mendixmodellib/reflection-data/# generate for specific version
go run cmd/generate/main.go -version=11.7.0 -output=generated/
# generate for latest version
go run cmd/generate/main.go -latest -output=generated/
# generate for all versions (creates version-specific packages)
go run cmd/generate/main.go -all -output=generated/# See what changed
git diff generated/
# check for breaking changes
go build ./...
go test ./...// version/versions.go
var SupportedVersions = []string{
"10.0.0",
"10.21.0",
"11.0.0",
"11.6.0",
"11.7.0", // NEW
}
var LatestVersion = "11.7.0"# .github/workflows/update-metamodel.yml
name: update Metamodel
on:
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch:
inputs:
version:
description: 'Mendix version to add'
required: true
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Setup node
uses: actions/setup-node@v4
- name: get latest mendixmodellib
run: |
npm pack mendixmodellib@latest
tar -xzf mendixmodellib-*.tgz
cp package/reflection-data/*.json libs/mendixmodellib/reflection-data/
- name: generate types
run: go run cmd/generate/main.go -latest -output=generated/
- name: run tests
run: go test ./...
- name: create PR
uses: peter-evans/create-pull-request@v6
with:
title: "update metamodel to latest version"
branch: update-metamodel- Create
cmd/generate/package - Implement JSON parser for reflection data
- Implement basic Go code emitter
- Generate
domainmodelspackage as proof of concept - Validate generated code compiles
- Generate
microflowswith all 150+ activity types - Generate
pageswith all 500+ widget types - Generate
workflowsdomain - Update parser to use generated types
- Generate remaining 48 domains
- Implement enum types
- Add interface generation for polymorphic types
- Add BSON tags for serialization
- Implement metamodel reflection system
- Add version compatibility checking
- Implement delta change tracking
- Add qualified name resolution
- Add comprehensive unit tests
- Test against real Mendix projects
- Document API usage
- Create migration guide from TypeScript SDK
| Domain | Types | Properties |
|---|---|---|
| pages | 312 | 2,847 |
| microflows | 156 | 1,203 |
| expressions | 89 | 412 |
| workflows | 78 | 534 |
| domainmodels | 52 | 389 |
| rest | 45 | 312 |
| customwidgets | 38 | 267 |
| reports | 36 | 289 |
| webservices | 34 | 245 |
| datatypes | 28 | 156 |
| ... | ... | ... |
| Total | ~820 | ~7,500 |
6.x: 6.0.0 → 6.10.4 (11 versions)
7.x: 7.0.0 → 7.23.0 (24 versions)
8.x: 8.0.0 → 8.18.0 (19 versions)
9.x: 9.0.0 → 9.24.0 (26 versions)
10.x: 10.0.0 → 10.21.0 (22 versions)
11.x: 11.0.0 → 11.6.0 (7 versions)
────────────────────────────────
Total: 88 versions supported