Schema Generation
Protean can generate standard JSON Schema (Draft 2020-12) documents for all data-carrying domain elements: aggregates, entities, value objects, commands, events, and projections.
Generated schemas are useful for:
- Contract testing — validate that payloads conform to expected shapes
- Schema registries — publish schemas to Confluent Schema Registry, AWS Glue, or similar
- OpenAPI integration — JSON Schema Draft 2020-12 is natively supported by OpenAPI 3.1
- Documentation — auto-generate API docs from schema definitions
- Code generation — generate client SDKs in other languages from schemas
Generating schemas
Use the protean schema generate command to produce JSON Schema files for
every data-carrying element in your domain.
From a live domain
protean schema generate --domain=my_app.domain
This loads the domain, builds the IR, and writes schemas to .protean/schemas/.
From an IR file
protean schema generate --ir=domain-ir.json
If you already have a serialized IR (from protean ir show), you can generate
schemas directly from it.
Custom output directory
protean schema generate --domain=my_app.domain --output=build
Other output formats
By default protean schema generate emits JSON Schema. Pass --format to emit
other contract formats — json, avro, protobuf, or all (which writes JSON
Schema, Avro, and Protobuf into one tree):
protean schema generate --domain=my_app.domain --format all
The Avro schema carries an aliases entry for any field declared with
renamed_from, so an external
Avro consumer resolves the old field name on the wire. See the
protean schema CLI reference for every flag, and
Evolving Events Over Time for how these formats fit the
wider event-evolution workflow.
Inspecting a single schema
Use protean schema show to print the JSON Schema for a specific element:
# By short name
protean schema show OrderPlaced --domain=my_app.domain
# By fully qualified name
protean schema show my_app.ordering.OrderPlaced --domain=my_app.domain
# Raw JSON for piping
protean schema show OrderPlaced --domain=my_app.domain --raw | jq .
If multiple elements share the same short name, the command lists all matching FQNs and asks you to disambiguate.
Output structure
Schemas are organized by aggregate cluster:
.protean/
├── ir.json
└── schemas/
├── Order/
│ ├── aggregates/
│ │ └── Order.v1.json
│ ├── commands/
│ │ └── PlaceOrder.v1.json
│ ├── entities/
│ │ └── LineItem.v1.json
│ ├── events/
│ │ └── OrderPlaced.v2.json
│ └── value_objects/
│ └── Money.v1.json
└── projections/
└── OrderDashboard.v1.json
Filenames include the element version (v1, v2, etc.) for events and
commands. Other element types default to v1.
Schema structure
Each generated schema is a standard JSON Schema object with Protean-specific
metadata in x-protean-* extension fields:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "OrderPlaced",
"type": "object",
"properties": {
"order_id": { "type": "string" },
"customer_name": { "type": "string" },
"total": { "type": "number" }
},
"required": ["customer_name", "order_id", "total"],
"x-protean-element-type": "event",
"x-protean-fqn": "my_app.ordering.OrderPlaced",
"x-protean-aggregate": "my_app.ordering.Order",
"x-protean-version": 1,
"x-protean-type": "Ordering.OrderPlaced.v1"
}
Extension fields
| Field | Description |
|---|---|
x-protean-element-type |
Element kind: aggregate, entity, value_object, command, event, projection |
x-protean-fqn |
Fully qualified name in the domain |
x-protean-aggregate |
FQN of the owning aggregate |
x-protean-version |
Event/command version number |
x-protean-type |
Message type string |
x-protean-published |
true if the event is part of the published contract |
x-protean-is-fact-event |
true if auto-generated from aggregate changes |
x-protean-auto-generated |
true if generated by the framework |
x-protean-identity-field |
Identity field name (aggregates only) |
x-protean-is-event-sourced |
true if the aggregate uses event sourcing |
Nested structures
Value objects and entities referenced by an aggregate are resolved as $defs
with $ref pointers:
{
"properties": {
"shipping_address": { "$ref": "#/$defs/ShippingAddress" }
},
"$defs": {
"ShippingAddress": {
"type": "object",
"properties": {
"street": { "type": "string", "maxLength": 255 },
"city": { "type": "string", "maxLength": 100 }
},
"required": ["city", "street"]
}
}
}
Optional fields
Optional fields use the anyOf pattern with null:
{
"total": {
"anyOf": [
{ "type": "number", "minimum": 0.0 },
{ "type": "null" }
]
}
}
Validating payloads
Generated schemas work with any JSON Schema validator. For example, using
Python's jsonschema library:
import json
import jsonschema
with open(".protean/schemas/Order/events/OrderPlaced.v1.json") as f:
schema = json.load(f)
payload = {
"order_id": "order-123",
"customer_name": "Alice",
"total": 99.99,
}
jsonschema.validate(payload, schema) # Passes