A folder a type organizes
A type can hold members through Organizes as well as through HasComponent
and HasProperty: a BuildingBlocks folder that groups the parts every
device of that type has. Machinery-based companion specifications use this
often — OPC 30500 LADS gives LADSDeviceType a MachineryBuildingBlocks folder
holding its state machines and lifetime counters.
Organizes is a hierarchical reference but not an aggregating one, and that
difference is what this page is about: which node is the folder's parent, what
its NodeId is called, and why it matters when the ids have to match a working
group's table.
Declaring it
objectTypes:
- browseName: PumpType
organizes:
- browseName: BuildingBlocks
typeDefinition: ua:FolderType
components:
- browseName: OperatingHours
dataType: ua:Double
The folder is a member of the type: it is Mandatory by default, like any member
declared in a type, and instances of PumpType get their own copy of it.
What the compiler emits
The type is the folder's declared parent. The NodeSet carries it as
ParentNodeId, and the folder and everything under it are named through the
type:
<UAObject NodeId="ns=1;i=1001" BrowseName="1:BuildingBlocks" ParentNodeId="ns=1;i=1000">
<DisplayName>BuildingBlocks</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="Organizes" IsForward="false">ns=1;i=1000</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=1002</Reference>
...
<UAVariable NodeId="ns=1;i=1002" BrowseName="1:OperatingHours" ParentNodeId="ns=1;i=1001" ...>
PumpType,1000,ObjectType
PumpType_BuildingBlocks,1001,Object
PumpType_BuildingBlocks_OperatingHours,1002,Variable
ValveType,1003,ObjectType
ValveType_BuildingBlocks,1004,Object
The reference that links them stays Organizes: the declaration names the
parent, it does not turn the folder into a component.
📄 Generated NodeSet2.xml — organized-folders.model.Nodeset2.xml
<?xml version="1.0"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" xmlns:ns1="http://sterfive.com/UA/doc-examples/organized-folders/Type.xsd">
<NamespaceUris>
<Uri>http://sterfive.com/UA/doc-examples/organized-folders/</Uri>
</NamespaceUris>
<Models>
<Model ModelUri="http://sterfive.com/UA/doc-examples/organized-folders/" Version="1.0.0" PublicationDate="2026-09-11T00:00:00.000Z">
<RequiredModel ModelUri="http://opcfoundation.org/UA/" Version="1.05.07" PublicationDate="2026-05-01T00:00:00.000Z"/>
</Model>
</Models>
<Aliases>
<Alias Alias="Double">i=11</Alias>
<Alias Alias="HasComponent">i=47</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="Organizes">i=35</Alias>
</Aliases>
<!--ReferenceTypes-->
<!--ObjectTypes-->
<!--ObjectType - 1:PumpType {{{{ -->
<UAObjectType NodeId="ns=1;i=1000" BrowseName="1:PumpType">
<DisplayName>PumpType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<!--ObjectType - 1:PumpType }}}}-->
<!--ObjectType - 1:ValveType {{{{ -->
<UAObjectType NodeId="ns=1;i=1003" BrowseName="1:ValveType">
<DisplayName>ValveType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<!--ObjectType - 1:ValveType }}}}-->
<!--VariableTypes-->
<!--Other Nodes-->
<!--Object - 1:BuildingBlocks {{{{ -->
<UAObject NodeId="ns=1;i=1001" BrowseName="1:BuildingBlocks" ParentNodeId="ns=1;i=1000">
<DisplayName>BuildingBlocks</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="Organizes" IsForward="false">ns=1;i=1000</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=1002</Reference>
</References>
</UAObject>
<UAVariable NodeId="ns=1;i=1002" BrowseName="1:OperatingHours" ParentNodeId="ns=1;i=1001" AccessLevel="3" DataType="Double">
<DisplayName>OperatingHours</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
</References>
</UAVariable>
<!--Object - 1:BuildingBlocks }}}} -->
<!--Object - 1:BuildingBlocks {{{{ -->
<UAObject NodeId="ns=1;i=1004" BrowseName="1:BuildingBlocks" ParentNodeId="ns=1;i=1003">
<DisplayName>BuildingBlocks</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="Organizes" IsForward="false">ns=1;i=1003</Reference>
</References>
</UAObject>
<!--Object - 1:BuildingBlocks }}}} -->
<!--Object - 1:Pump1 {{{{ -->
<UAObject NodeId="ns=1;i=1005" BrowseName="1:Pump1">
<DisplayName>Pump1</DisplayName>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
<Reference ReferenceType="HasTypeDefinition">ns=1;i=1000</Reference>
</References>
</UAObject>
<!--Object - 1:BuildingBlocks {{{{ -->
<UAObject NodeId="ns=1;i=1006" BrowseName="1:BuildingBlocks">
<DisplayName>BuildingBlocks</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
<Reference ReferenceType="Organizes" IsForward="false">ns=1;i=1005</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=1007</Reference>
</References>
</UAObject>
<UAVariable NodeId="ns=1;i=1007" BrowseName="1:OperatingHours" ParentNodeId="ns=1;i=1006" AccessLevel="3" DataType="Double">
<DisplayName>OperatingHours</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
</References>
</UAVariable>
<!--Object - 1:BuildingBlocks }}}} -->
<!--Object - 1:Pump1 }}}} -->
</UANodeSet>
Why the name matters: a working group's table
This is exactly what the OPC Foundation's ModelCompiler writes. In OPC 30500
LADS, the folder LADSDeviceType organizes is declared with the type as its
ParentNodeId, and the working group's NodeIds.csv names it — and every node
below it — through the type:
<UAObject NodeId="ns=4;i=5063" BrowseName="3:MachineryBuildingBlocks" ParentNodeId="ns=4;i=1002">
<References>
<Reference ReferenceType="Organizes" IsForward="false">ns=4;i=1002</Reference>
LADSDeviceType_MachineryBuildingBlocks,5063,Object
LADSDeviceType_MachineryBuildingBlocks_LifetimeCounters,5091,Object
opcua-modeler spec-repo pull reverses that NodeSet into a model, seeds the
NodeIds from that table and compiles it back. For the ids to be reused, the
compiled folder must carry the same name as the row: LADSDeviceType_…, not
MachineryBuildingBlocks. With the declared parent it does, and the LADS pull
reproduces all 650 ids of the group's table.
The two traps this closes
The orphan organized folder. Without a declared parent, an organized folder
has no parent at all, since Organizes does not aggregate. It was named on its
own (BuildingBlocks), the table's PumpType_BuildingBlocks row was never
matched, and a second type organizing a folder of the same name —
ValveType above — collided with the first and got no stable id.
objectTypes:
- browseName: ValveType
organizes:
- browseName: BuildingBlocks
typeDefinition: ua:FolderType
The first-reference parent. A node reached through two aggregating parents
used to take the first reference met as its parent. LADS has one such node per
nameplate property: the type's own Manufacturer is also a property of its
Identification add-in. The NodeSet declares the type, and the reverse now
writes the node under the type, with the add-in's link as a references:
entry, so it is compiled once, named after the type, and keeps the group's id.
An existing model keeps its NodeIds
A symbol table written before this change (a .symbols.csv, or the table of a
.opcua.yaml) names an organized folder without its type. When the compiler
finds such a row and no row under the new name, it moves the row to the new
name — the folder and everything below it — so no node is renumbered. The ids
do not change; only the names in the table do, the next time it is written.
What an instance gets
instances:
- browseName: Pump1
typeDefinition: PumpType
organizedBy: /ua:Objects
Pump1 gets its own BuildingBlocks folder, as instantiating a type creates
its Mandatory members. An instance organized by the Objects folder does not
declare a parent: Objects belongs to the standard namespace, and the instance
keeps its own name.
The instance's copy of the folder is not yet named after the instance: it is
BuildingBlocks, not Pump1_BuildingBlocks, and a second instance's copy gets
no symbol of its own. The copy is made by node-opcua when it instantiates the
type, and does not declare the instance as its parent yet.