InlineStructDefs
LittleHorse 1.3 adds InlineStructDef as a first-class type definition. An inline Struct schema is embedded directly in the WfSpec, TaskDef, ExternalEventDef, or containing Struct schema that uses it. You do not register it as a separate metadata object.
Use an inline Struct when a structured type belongs to one metadata definition and does not need its own name, version, or compatibility policy. Use a named StructDef when the schema should be registered, reused, and versioned independently.
The Java SDK supports the Inline StructDef APIs described on this page as of LittleHorse 1.3.
How InlineStructDefs Work
Every input, output, and workflow variable in LittleHorse has a TypeDefinition. For an inline Struct, the inline_struct_def option contains the schema at the point of use:
TypeDefinition
└── inline_struct_def
└── fields
├── street: StructFieldDef(STR)
├── city: StructFieldDef(STR)
└── postalCode: StructFieldDef(STR)
Each StructFieldDef defines a field's type and can also define a default value, nullability, and a human-readable description. Because those field types are also TypeDefinitions, inline Struct schemas can be nested.
Unlike a named StructDef, an InlineStructDef has no metadata ID or independent version. Its lifecycle follows the WfSpec, TaskDef, ExternalEventDef, or named StructDef that contains it.
Embed a POJO in a Named StructDef
In Java, a class annotated with @LHStructDef defines a named schema. An unannotated POJO used as one of its fields defines an inline schema.
The following Order class is registered as the named order StructDef:
import io.littlehorse.sdk.worker.LHStructDef;
import io.littlehorse.sdk.worker.LHStructField;
@LHStructDef(
value = "order",
description = "A customer order with a delivery address."
)
public class Order {
@LHStructField(description = "A unique identifier for this order.")
private String orderId;
@LHStructField(description = "The destination address for the shipment.")
private DeliveryAddress deliveryAddress;
// Constructors, getters, and setters...
}
DeliveryAddress has no class-level @LHStructDef annotation, so the Java SDK embeds its schema in the deliveryAddress field instead of creating a reference to another named StructDef:
import io.littlehorse.sdk.worker.LHStructField;
public class DeliveryAddress {
@LHStructField(description = "The street address for the destination.")
private String street;
@LHStructField(description = "The destination city.")
private String city;
@LHStructField(description = "The destination postal code.")
private String postalCode;
// Constructors, getters, and setters...
}
Register only the named Order schema:
LHStructDefType orderType = new LHStructDefType(Order.class);
config.getBlockingStub().putStructDef(orderType.toPutStructDefRequest());
Only the order StructDef is registered. Its deliveryAddress field contains the DeliveryAddress schema directly.
Other Supported Use Cases
You can embed an InlineStructDef directly in a WfSpec variable or TaskDef signature.
InlineStructDefs do not provide the same versioning and evolution guarantees as a named StructDef, and thus should be used with caution inside of WfSpecs and TaskDefs.
Prefer a named StructDef for contracts that need to evolve across workflows, tasks, or independently deployed producers and consumers. Use direct embedding for local schemas whose lifecycle is intentionally coupled to one WfSpec or TaskDef.
Use in WfSpecs
Use WorkflowThread#declareInlineStruct() to embed a POJO-derived schema directly in a WfSpec variable definition:
Workflow workflow = new WorkflowImpl("normalize-address", wf -> {
WfRunVariable address = wf
.declareInlineStruct("address", DeliveryAddress.class)
.required();
address.assign(wf.execute("normalize-address", address));
});
This declaration does not look up or register a DeliveryAddress StructDef. The compiled WfSpec stores the reflected schema in the variable's TypeDefinition.
Use in TaskDefs
For a top-level task parameter or return value, annotate the relevant target with @LHType(isInlineStruct = true). Apply the annotation to a parameter for task input and to the method for task output:
import io.littlehorse.sdk.worker.LHTaskMethod;
import io.littlehorse.sdk.worker.LHType;
public class AddressWorker {
@LHTaskMethod("normalize-address")
@LHType(isInlineStruct = true)
public DeliveryAddress normalizeAddress(
@LHType(isInlineStruct = true) DeliveryAddress address
) {
return new DeliveryAddress(
address.getStreet().trim(),
address.getCity().trim(),
address.getPostalCode().trim()
);
}
}
The worker registration embeds the reflected DeliveryAddress schema in both the TaskDef input parameter and return type. At runtime, the SDK converts between the POJO and the validated LittleHorse Struct value.
An unannotated POJO in a task signature retains the SDK's legacy JSON behavior. Use @LHType(isInlineStruct = true) when the top-level task input or output must be a schema-validated inline Struct.
Further Resources
- StructDefs and Structs — reusable, independently versioned Struct schemas.
- Variables — declaring and using workflow variables.
- Arrays — strongly typed ordered collections.
- Maps — strongly typed key/value collections.
- Inline StructDef Java example — complete registration, worker, and workflow source.
- API Reference: InlineStructDef — protobuf schema for an embedded Struct definition.