Skip to content

Collection Semantics

When a resource property contains a collection (list of values or objects), formae needs to know how to compare the current state with the desired state. The updateMethod field hint controls this behavior.

The Three Collection Types

Set (Default)

When no updateMethod is specified, collections are treated as unordered sets. Elements are compared regardless of their position in the list.

@FieldHint {}
securityGroups: Listing<String>?

Behavior:

  • Order doesn't matter: ["a", "b", "c"] equals ["c", "a", "b"]
  • Duplicates are ignored
  • Adding an element that already exists results in no change
  • In reconcile mode, elements not in the desired state are removed

Use for: Simple lists where order doesn't matter (security group IDs, availability zones, etc.)

Array

Use updateMethod = "Array" when the order of elements matters.

@FieldHint {
    updateMethod = "Array"
}
processingSteps: Listing<String>?

Behavior:

  • Position-based comparison
  • ["a", "b"] does NOT equal ["b", "a"]
  • Elements at the same index are compared directly

Use for: Ordered lists where sequence is significant (processing pipelines, priority lists, etc.)

EntitySet

Use updateMethod = "EntitySet" with indexField for collections of objects identified by a key.

@FieldHint {
    updateMethod = "EntitySet"
    indexField = "Key"
}
tags: Listing<Tag>?

Behavior:

  • Objects are matched by their key field (e.g., Key for tags)
  • If an object with the same key exists, only changed properties are updated
  • New keys result in add operations
  • Missing keys in reconcile mode result in remove operations

Use for: Key-value collections like tags, environment variables, or any list of objects with a unique identifier.

Example: AWS Tags

The most common use of EntitySet is for resource tags:

// Plain class - no FieldHints needed on tag properties
open class Tag {
    hidden key: String
    hidden value: Any

    fixed Key: String = key
    fixed Value: Any = value
}

@ResourceHint {
    type = "AWS::EC2::VPC"
    identifier = "VpcId"
}
open class Vpc extends formae.Resource {
    @FieldHint {
        updateMethod = "EntitySet"
        indexField = "Key"
    }
    tags: Listing<Tag>?
}

With this schema, updating a tag value results in a targeted update that only changes the value, not the entire tag.

How It Works

When formae compares current state with desired state, it uses the updateMethod hint to determine the comparison strategy:

  1. No hint or Set: Compare collections as unordered sets
  2. Array: Compare collections position-by-position
  3. EntitySet: Match objects by indexField, then compare matched objects property-by-property

Reconcile vs Patch Mode

The apply mode significantly affects how collections are handled:

Reconcile mode (--mode reconcile, default): The desired state is the complete truth. Elements not in your forma file are removed from the actual resource.

Patch mode (--mode patch): Collections are append-only. Elements in your forma file are added if missing, but existing elements are never removed.

This append-only behavior in patch mode applies to all three collection types:

Collection Type Reconcile Mode Patch Mode
Set Elements not in desired state are removed New elements added, existing preserved
Array Elements not in desired state are removed New elements appended, existing preserved
EntitySet Keys not in desired state are removed New keys added, existing keys preserved

Example: If the actual resource has tags [A, B, C] and your forma file specifies [B, D]:

  • Reconcile: Result is [B, D] (A and C removed)
  • Patch: Result is [A, B, C, D] (D added, nothing removed)

Choosing the Right Semantics

Scenario updateMethod indexField
List of primitive values, order irrelevant (none) -
List of primitive values, order matters Array -
List of objects with unique key EntitySet Key field name
List of objects without unique key (none) -

When in doubt, start with the default (Set) semantics. Only specify Array or EntitySet when you have a specific reason.