organize-project · git:20260821.ff81a24 · 2026-08-21 · sha256 1d65561855a1f1db
organize-project git:20260821.ff81a24A
Immutable. This exact content is served forever at /api/v1/blob/1d65561855a1f1db.
---
name: organize-project
description: "Organise documents into folders and move them between folders and modules with MOVE. Use when a module has grown unstructured, when restructuring a project, or when a document is in the wrong place."
---
# Project Organization: Folders and Moving Documents
This skill covers organizing Mendix project documents (pages, microflows, snippets, nanoflows) into folders and moving them between folders and modules.
## When to Use This Skill
Use this skill when:
- Organizing documents into folder hierarchies within a module
- Moving documents between folders
- Moving documents between modules
- Restructuring a project for better maintainability
- Setting up folder conventions for a new module
## Folder Conventions
Organize by **functional grouping** — keep all artifacts for a feature together, not separated by document type. This way, a developer working on "Customer" finds everything in one place: pages, microflows, snippets, and validation logic.
Recommended folder structure within a module:
```
CRM/
├── Customer/
│ ├── Customer_Overview -- Overview page
│ ├── Customer_NewEdit -- Edit page
│ ├── CustomerCard -- Snippet
│ ├── ACT_Customer_Save -- Save microflow
│ ├── ACT_Customer_Delete -- Delete microflow
│ ├── ACT_Customer_New -- New microflow
│ ├── VAL_Customer -- Validation microflow
│ └── DS_Customer_Filter -- Data source microflow
├── Order/
│ ├── Order_Overview
│ ├── Order_NewEdit
│ ├── ACT_Order_Save
│ └── VAL_Order
└── Shared/ -- Cross-cutting concerns
├── SUB_SendNotification
└── Navigation_Snippet
```
**Why functional grouping over type grouping:**
- All related artifacts are in one place — easier to navigate and review
- Adding or removing a feature is a single folder operation
- Naming prefixes (ACT_, VAL_, SUB_, DS_) already indicate document type
- Mirrors how developers think: "I'm working on Customer" not "I'm working on microflows"
Adapt to your project's conventions. The key is consistency across modules.
## Creating Documents in Folders
### Microflows
Use the `folder` keyword after the return type, before `begin`:
```mdl
create microflow MyModule.ACT_ProcessOrder ($Order: MyModule.Order)
returns boolean as $success
folder 'Order'
begin
commit $Order;
return true;
end;
```
### Pages
Use the `folder` property inside the page properties:
```sql
create page MyModule.Customer_Overview
(
title: 'Customer Overview',
layout: Atlas_Core.Atlas_Default,
folder: 'Customer'
)
{
-- widgets
}
```
### Snippets
```sql
create snippet MyModule.CustomerCard
(
folder: 'Customer'
)
{
-- widgets
}
```
### Nested Folders
Use `/` to create nested folder paths. Missing folders are created automatically:
```mdl
-- Creates 'Order', then 'Order/Batch' if they don't exist
create microflow MyModule.ACT_BatchProcess ($list: list of MyModule.Order)
folder 'Order/Batch'
begin
loop $Order in $list begin
commit $Order;
end loop;
return;
end;
```
## Reading the Layout Back
`list folders` shows the folder layout of a module and what is in each folder.
This is the counterpart to `move`: `move` puts a document somewhere, `list
folders` shows where everything actually is.
```sql
-- One module
list folders in MyModule;
-- Every module in the project
list folders;
```
```
MyModule
(module root) [1]
Microflow ACT_Unfiled
Api [0]
Api/Published [1]
ODataService PublicApi
Support [1]
JavaAction Helper
(3 folder(s), 3 document(s))
```
Three things about the output are deliberate:
- **Empty folders are listed** (`Api [0]`), so the listing is the whole layout
and can be diffed against an intended one.
- **Documents still at the module root** appear under `(module root)` — what is
not filed yet is the thing you most want to notice.
- **Ordering is stable**, so a diff between two runs shows only real movement.
Use the CLI's `--json` flag for a row per document (`Module, Folder, Kind, Document`)
when comparing against a checked-in layout.
Do **not** reach for `show structure` here: it groups by document type at every
depth and never shows which folder a document sits in.
## Moving Documents
The `move` command relocates existing documents between folders and modules.
### Move to a Folder (Same Module)
```mdl
move page MyModule.CustomerEdit to folder 'Customer';
move microflow MyModule.ACT_ProcessOrder to folder 'Order';
move snippet MyModule.NavigationMenu to folder 'Shared';
move nanoflow MyModule.NAV_OpenCustomer to folder 'Customer';
move enumeration MyModule.OrderStatus to folder 'Shared';
```
### Move to Module Root (Out of Folder)
```mdl
move page MyModule.CustomerEdit to MyModule;
```
### Move Across Modules
```mdl
-- Move to another module's root
move page OldModule.CustomerPage to NewModule;
-- Move to a folder in another module
move page OldModule.CustomerPage to folder 'Pages' in NewModule;
```
### Cross-Module Move Warning
Cross-module moves change the qualified name (e.g., `OldModule.CustomerPage` becomes `NewModule.CustomerPage`). This **breaks by-name references** such as:
- Microflows calling `show page OldModule.CustomerPage`
- Other microflows calling `call microflow OldModule.SomeMicroflow`
- Widget actions referencing the old qualified name
**Always check impact before cross-module moves:**
```mdl
show impact of OldModule.CustomerPage;
-- Review the output, then move if safe:
move page OldModule.CustomerPage to NewModule;
```
## Folder Rules
- Folder names are **case-sensitive**
- Use `/` as separator for nested folders: `'Parent/Child/Grandchild'`
- Folders are **created automatically** if they don't exist
- Moving to a folder that doesn't exist creates it
- Empty folders are preserved in the project
## Supported Document Types
`move` accepts **every top-level document type**, spelled the way `describe`
spells it:
| Group | Types |
|-------|-------|
| Pages | `page`, `snippet`, `building block`, `layout`, `menu` |
| Logic | `microflow`, `nanoflow`, `workflow`, `queue`, `scheduled event` |
| Domain | `enumeration`, `constant`, `regular expression` |
| Mappings | `json structure`, `import mapping`, `export mapping` |
| Code | `java action`, `javascript action`, `database connection`, `data transformer` |
| Resources | `image collection`, `icon collection` |
| Integration | `rest client`, `published rest service`, `odata client`, `odata service`, `business event service` |
| AI | `model`, `agent`, `knowledge base`, `consumed mcp service` |
`move entity` is the exception: an entity lives inside a domain model, so it
moves between **modules** only, never into a folder.
If the named document turns out to be a different type, the statement is refused
and the error names what it really is — `move queue Mod.JSON_Order` reports that
`Mod.JSON_Order` is a json structure.
### FOLDER on Create
Every document type takes a folder clause on `create`, so a document can be
placed in the statement that creates it rather than in a separate `move`. Where
the clause goes depends on the statement's shape:
| Document Type | FOLDER on Create |
|---------------|-----------------|
| Page, Snippet | `folder: 'path'` — a property, inside the parentheses |
| Microflow, Nanoflow | `folder 'path'` — a keyword, before `begin` |
| Enumeration, Constant | `folder 'path'` — a keyword, after the definition |
| Everything else | `folder 'path'` — a keyword, straight after the qualified name |
```mdl
create import mapping CRM.IMM_Order folder 'Private/Import mappings'
with json structure CRM.JSON_Order { create CRM.Order { Id = id } };
create queue CRM.Q_Orders folder 'Private/Queues' ( Parallelism: 3 );
create java action CRM.JA_Sync folder 'Private/Java' () returns string
as $$return null;$$;
```
**A folder clause on `create or modify` moves an existing document.** It used to
be silently ignored: the statement reported success, the folder was created, and
the document stayed where it was (#932). Omitting the clause leaves placement
alone — it never returns a document to the module root — so adding a folder to a
script is safe and removing it is a no-op.
`describe` emits the clause, so a description replays into the same folder
rather than into the module root.
## Example: Reorganize a Module
```mdl
-- Group all Customer artifacts together
move page CRM.Customer_Overview to folder 'Customer';
move page CRM.Customer_NewEdit to folder 'Customer';
move microflow CRM.ACT_Customer_Save to folder 'Customer';
move microflow CRM.ACT_Customer_Delete to folder 'Customer';
move microflow CRM.ACT_Customer_New to folder 'Customer';
move microflow CRM.VAL_Customer to folder 'Customer';
move snippet CRM.CustomerCard to folder 'Customer';
-- Group all Order artifacts together
move page CRM.Order_Overview to folder 'Order';
move page CRM.Order_NewEdit to folder 'Order';
move microflow CRM.ACT_Order_Save to folder 'Order';
move microflow CRM.ACT_Order_Process to folder 'Order/Processing';
-- Move shared artifacts to a Shared folder or common module
show impact of CRM.Header_Snippet;
move snippet CRM.Header_Snippet to folder 'Shared' in Common;
-- Move entity to different module
show impact of CRM.Customer;
move entity CRM.Customer to CustomerModule;
-- Move enumeration to different module
move enumeration CRM.OrderStatus to SharedModule;
```
## Moving Folders
Use `move folder` to reorganize folders. Syntax matches document moves: `Module.FolderName`.
```sql
-- Move a folder into another folder
move folder MyModule.Resources to folder 'Archive';
-- Move a nested folder (use double quotes for paths with /)
move folder MyModule."Orders/Archive" to MyModule;
-- Move a folder to a different module
move folder MyModule.SharedWidgets to CommonModule;
-- Move a folder into a folder in another module
move folder MyModule.Templates to folder 'Shared' in CommonModule;
```
## Deleting Folders
Use `drop folder` to remove empty folders. The folder must not contain any documents or sub-folders.
```sql
-- Drop an empty folder
drop folder 'OldPages' in MyModule;
-- Drop a nested folder (only the leaf is removed)
drop folder 'Orders/Archive' in MyModule;
-- Move contents out first, then drop
move microflow MyModule.ACT_Process to MyModule;
drop folder 'Processing' in MyModule;
```
## Validation Checklist
- [ ] Folder paths use `/` separator (not `\`)
- [ ] FOLDER keyword placement is correct (before BEGIN for microflows, inside properties for pages)
- [ ] Cross-module moves: checked impact with `show impact of` first
- [ ] Folder naming is consistent across modules
- [ ] DROP FOLDER: verify folder is empty before dropping
- [ ] After a batch of moves: `list folders in MyModule` to confirm the layout