Build CoreOrdered learning track

XSD Foundations for Contract Design

Learn Java XML In Action - Part 010

Fondasi XSD untuk desain contract: schema sebagai executable grammar, element/type design, namespace, occurrence, composition, identity, validation semantics, compatibility, dan production-grade schema modelling.

13 min read2469 words
PrevNext
Lesson 1032 lesson track0718 Build Core
#java#xml#xsd#schema+5 more

Part 010 — XSD Foundations for Contract Design

Tujuan Part Ini

Part ini membahas XSD sebagai contract design language, bukan sekadar file validasi.

Setelah part ini, targetnya kamu bisa:

  • membaca XSD dengan cepat;
  • mendesain schema yang stabil;
  • membedakan element, type, complex type, simple type, group, dan attribute;
  • memahami minOccurs, maxOccurs, optionality, ordering, dan namespace;
  • menghindari schema yang terlalu kaku atau terlalu longgar;
  • merancang XML contract yang bisa berevolusi;
  • menyiapkan mental model sebelum masuk ke datatype/facet/modularization di part berikutnya.

XSD adalah executable contract:

XSD = grammar + constraints + namespace contract + compatibility boundary

1. Why XSD Matters in Production

Di sistem enterprise, XML jarang hanya menjadi format data. Ia sering menjadi dokumen kontraktual.

Contoh:

  • order submission;
  • insurance claim;
  • regulatory filing;
  • invoice;
  • payment instruction;
  • healthcare message;
  • telco provisioning request;
  • government document exchange;
  • partner batch feed.

Dalam konteks seperti ini, XSD menjawab pertanyaan:

QuestionXSD Role
Field apa yang wajib?minOccurs, required attribute
Urutan elemen seperti apa?compositor sequence
Field boleh muncul berapa kali?maxOccurs
Nilai punya format apa?simple type + facet
Struktur nested seperti apa?complex type
Namespace dokumen apa?targetNamespace
Apa yang boleh diextend?extension point/type derivation
Versi contract apa?namespace/versioning policy

XSD bukan pengganti business rule, tetapi XSD adalah gate pertama untuk structural correctness.


2. Three Levels of Correctness

Sebelum menulis XSD, bedakan tiga level correctness.

LevelMeaningExample
Well-formed XMLXML sintaks validtag tertutup, quote benar, satu root
Schema-valid XMLXML sesuai XSDorderId wajib, lineItem minimal satu
Semantically-valid documentBenar secara bisnisorder total cocok dengan price calculation

Contoh XML well-formed tapi tidak schema-valid:

<Order xmlns="urn:commerce:order:v1">
  <CustomerId>C-100</CustomerId>
</Order>

Jika XSD mewajibkan OrderId, dokumen ini tidak valid.

Contoh XML schema-valid tapi tidak semantically valid:

<Order xmlns="urn:commerce:order:v1">
  <OrderId>O-100</OrderId>
  <CustomerId>C-100</CustomerId>
  <Currency>USD</Currency>
  <LineItems>
    <LineItem>
      <Sku>SKU-1</Sku>
      <Quantity>2</Quantity>
      <UnitPrice>10.00</UnitPrice>
      <LineTotal>999.00</LineTotal>
    </LineItem>
  </LineItems>
  <OrderTotal>20.00</OrderTotal>
</Order>

XSD bisa memastikan LineTotal adalah decimal. Tetapi XSD biasanya bukan tempat ideal untuk menghitung apakah 2 * 10.00 == 999.00.

Production rule:

Let XSD enforce structure and lexical/value constraints. Let domain logic enforce cross-field and stateful business semantics.


3. Minimal XSD Example

XML:

<Order xmlns="urn:commerce:order:v1">
  <OrderId>O-100</OrderId>
  <CustomerId>C-200</CustomerId>
</Order>

XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="urn:commerce:order:v1"
    xmlns="urn:commerce:order:v1"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">

  <xs:element name="Order" type="OrderType"/>

  <xs:complexType name="OrderType">
    <xs:sequence>
      <xs:element name="OrderId" type="xs:string"/>
      <xs:element name="CustomerId" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

</xs:schema>

Key observations:

XSD PartMeaning
xs:schemaroot schema declaration
targetNamespacenamespace untuk vocabulary yang didefinisikan
xmlns="urn:commerce:order:v1"default namespace untuk reference lokal di XSD
elementFormDefault="qualified"local elements harus berada di target namespace
xs:element name="Order"global root element
type="OrderType"root memakai named complex type
xs:complexTypestruktur element dengan children/attributes
xs:sequencechildren harus muncul dalam urutan tertentu

4. Schema as Grammar

XSD mendefinisikan grammar: element apa boleh muncul, di mana, dan berapa kali.

XSD grammar-nya:

<xs:complexType name="OrderType">
  <xs:sequence>
    <xs:element name="OrderId" type="OrderIdType"/>
    <xs:element name="CustomerId" type="CustomerIdType"/>
    <xs:element name="LineItems" type="LineItemsType"/>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="LineItemsType">
  <xs:sequence>
    <xs:element name="LineItem" type="LineItemType" minOccurs="1" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="LineItemType">
  <xs:sequence>
    <xs:element name="Sku" type="xs:string"/>
    <xs:element name="Quantity" type="xs:positiveInteger"/>
    <xs:element name="UnitPrice" type="xs:decimal"/>
  </xs:sequence>
</xs:complexType>

This is not merely validation. It is a shared grammar between producer and consumer.


5. Element vs Type

Salah satu konsep XSD paling penting:

Element = named thing appearing in XML document
Type    = reusable definition of allowed content/value

Example:

<xs:element name="OrderId" type="OrderIdType"/>

<xs:simpleType name="OrderIdType">
  <xs:restriction base="xs:string">
    <xs:minLength value="1"/>
    <xs:maxLength value="40"/>
  </xs:restriction>
</xs:simpleType>

OrderId adalah element. OrderIdType adalah type.

Why it matters:

Design ChoiceImpact
Reusing typeKonsistensi constraint antar element
Reusing elementGlobal element bisa dipakai untuk substitution/ref
Inline anonymous typeSimpler untuk local-only structure
Named typeLebih mudah reuse, document, dan evolve

6. Global vs Local Declarations

XSD punya dua gaya utama.

6.1 Global Element + Named Type

<xs:element name="Order" type="OrderType"/>

<xs:complexType name="OrderType">
  <xs:sequence>
    <xs:element name="OrderId" type="OrderIdType"/>
  </xs:sequence>
</xs:complexType>

Kelebihan:

  • reusable;
  • easier documentation;
  • compatible with schema-first tooling;
  • root element jelas;
  • type bisa diextend/restrict dalam model tertentu.

Kekurangan:

  • schema lebih verbose;
  • terlalu banyak global type bisa membuat namespace penuh.

6.2 Local Anonymous Type

<xs:element name="Order">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="OrderId" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Kelebihan:

  • concise untuk dokumen kecil;
  • local structure mudah dibaca.

Kekurangan:

  • sulit reuse;
  • sulit reference;
  • generated code bisa kurang stabil;
  • refactoring contract lebih mahal.

Production recommendation:

Use global root elements and named business types for stable contracts.
Use local anonymous types only for simple one-off internal structures.

7. Namespace Design

Namespace adalah identitas vocabulary.

targetNamespace="urn:commerce:order:v1"

XML instance:

<Order xmlns="urn:commerce:order:v1">
  <OrderId>O-100</OrderId>
</Order>

7.1 elementFormDefault

Dalam schema production, biasanya gunakan:

elementFormDefault="qualified"
attributeFormDefault="unqualified"

Artinya local elements juga harus masuk target namespace.

Why this is usually better:

  • XML instance lebih konsisten;
  • XPath lebih predictable;
  • cross-namespace integration lebih jelas;
  • mengurangi bug karena element tidak berada di namespace yang diharapkan.

7.2 Namespace Anti-Pattern

Bad:

<Order xmlns="urn:commerce:order:v1">
  <OrderId>O-100</OrderId>
  <ext:PartnerData xmlns:ext="urn:partner:any">
    <Code>ABC</Code>
  </ext:PartnerData>
</Order>

Code berada di default namespace urn:commerce:order:v1 atau inheritance default namespace tergantung context. Sering membuat XPath dan validation membingungkan.

Better:

<Order xmlns="urn:commerce:order:v1"
       xmlns:ext="urn:partner:any">
  <OrderId>O-100</OrderId>
  <ext:PartnerData>
    <ext:Code>ABC</ext:Code>
  </ext:PartnerData>
</Order>

Rule:

In mixed-namespace documents, be explicit. Prefix extension vocabularies consistently.


8. Occurrence Constraints

Default minOccurs dan maxOccurs adalah 1.

<xs:element name="OrderId" type="xs:string"/>

Sama dengan:

<xs:element name="OrderId" type="xs:string" minOccurs="1" maxOccurs="1"/>

8.1 Optional Element

<xs:element name="CustomerReference" type="xs:string" minOccurs="0"/>

Artinya element boleh tidak ada.

8.2 Repeated Element

<xs:element name="LineItem" type="LineItemType" minOccurs="1" maxOccurs="unbounded"/>

Artinya minimal satu LineItem, jumlah maksimum tidak dibatasi oleh XSD.

Production caution:

maxOccurs="unbounded" is a contract statement, not a capacity plan.

Walaupun schema mengizinkan unbounded, service tetap harus punya operational limit.

8.3 Empty List vs Missing List

Perhatikan perbedaan:

<Order>
  <LineItems/>
</Order>

vs

<Order>
</Order>

Jika LineItems required tapi LineItem optional, empty list valid.

<xs:element name="LineItems" type="LineItemsType" minOccurs="1"/>

<xs:complexType name="LineItemsType">
  <xs:sequence>
    <xs:element name="LineItem" type="LineItemType" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

Jika business membutuhkan minimal satu item, set LineItem minOccurs="1".


9. Compositors: sequence, choice, all

9.1 sequence

sequence berarti child elements harus muncul dalam urutan tertentu.

<xs:sequence>
  <xs:element name="OrderId" type="xs:string"/>
  <xs:element name="CustomerId" type="xs:string"/>
  <xs:element name="OrderDate" type="xs:date"/>
</xs:sequence>

Valid:

<Order>
  <OrderId>O-100</OrderId>
  <CustomerId>C-100</CustomerId>
  <OrderDate>2026-07-02</OrderDate>
</Order>

Invalid jika urutan berubah.

9.2 choice

choice berarti salah satu branch.

<xs:choice>
  <xs:element name="PersonCustomer" type="PersonCustomerType"/>
  <xs:element name="OrganizationCustomer" type="OrganizationCustomerType"/>
</xs:choice>

Useful untuk mutually exclusive variants.

9.3 all

all mengizinkan elemen muncul tanpa urutan tertentu, tetapi penggunaannya lebih terbatas.

<xs:all>
  <xs:element name="OrderId" type="xs:string"/>
  <xs:element name="CustomerId" type="xs:string"/>
</xs:all>

Use carefully. Banyak contract enterprise tetap memakai sequence untuk determinism.


10. Attributes vs Elements

Pertanyaan umum:

Harus pakai element atau attribute?

Tidak ada jawaban absolut, tetapi ada guideline.

10.1 Use Element For Business Data

<Order>
  <OrderId>O-100</OrderId>
  <CustomerId>C-200</CustomerId>
  <OrderDate>2026-07-02</OrderDate>
</Order>

Cocok untuk:

  • domain fields;
  • data yang bisa nested;
  • data yang mungkin punya metadata;
  • data yang mungkin berulang;
  • data yang mungkin evolve.

10.2 Use Attribute For Metadata/Qualifier

<Amount currency="USD">100.00</Amount>

Cocok untuk:

  • identifiers metadata;
  • qualifiers;
  • flags kecil;
  • language/code annotations;
  • version marker tertentu.

XSD:

<xs:complexType name="AmountType">
  <xs:simpleContent>
    <xs:extension base="xs:decimal">
      <xs:attribute name="currency" type="CurrencyCodeType" use="required"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>

10.3 Attribute Limitations

Attributes:

  • cannot repeat with same name;
  • cannot contain child elements;
  • are less convenient for future structural extension;
  • do not preserve ordering semantics;
  • can be awkward in object binding.

Production guideline:

Default to elements for domain data. Use attributes for compact metadata and qualifiers.

11. Simple Type vs Complex Type

11.1 Simple Type

A simple type has text-only value.

<xs:simpleType name="CurrencyCodeType">
  <xs:restriction base="xs:string">
    <xs:pattern value="[A-Z]{3}"/>
  </xs:restriction>
</xs:simpleType>

11.2 Complex Type

A complex type can contain elements and/or attributes.

<xs:complexType name="MoneyType">
  <xs:sequence>
    <xs:element name="Amount" type="xs:decimal"/>
    <xs:element name="Currency" type="CurrencyCodeType"/>
  </xs:sequence>
</xs:complexType>

11.3 Simple Content Extension

For value + attribute:

<xs:complexType name="AmountType">
  <xs:simpleContent>
    <xs:extension base="xs:decimal">
      <xs:attribute name="currency" type="CurrencyCodeType" use="required"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>

XML:

<Amount currency="USD">100.00</Amount>

Good for compact document. But for extensibility, MoneyType with child elements may be better.


12. Nillable vs Optional

These are different.

12.1 Optional

<xs:element name="MiddleName" type="xs:string" minOccurs="0"/>

Element may be absent.

12.2 Nillable

<xs:element name="MiddleName" type="xs:string" nillable="true"/>

Element exists but explicitly has nil value.

XML:

<MiddleName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>

Meaning difference:

FormMeaning
Missing elementNot provided / not applicable / unknown depending contract
xsi:nil="true"Explicitly provided as nil
Empty element <MiddleName/>Present with empty string or empty content depending type

Production warning:

Do not use optional, nil, and empty string interchangeably.

Define semantics in contract documentation.


13. Defaults and Fixed Values

XSD supports default/fixed values.

<xs:element name="Priority" type="xs:string" default="NORMAL"/>
<xs:attribute name="version" type="xs:string" fixed="1.0"/>

Be careful.

Default values can make validation output differ from raw input depending processor behavior and post-schema validation infoset assumptions.

Production guideline:

  • Use explicit values in payload for important business fields.
  • Use fixed for protocol constants if truly stable.
  • Avoid relying on defaults for domain decision making unless all consumers agree.

14. Root Element Strategy

A production schema should make root elements explicit.

<xs:element name="SubmitOrderRequest" type="SubmitOrderRequestType"/>
<xs:element name="SubmitOrderResponse" type="SubmitOrderResponseType"/>
<xs:element name="OrderStatusNotification" type="OrderStatusNotificationType"/>

Avoid ambiguous generic roots:

<xs:element name="Message" type="MessageType"/>

unless there is a clear envelope protocol.

Better envelope pattern:

<Envelope xmlns="urn:platform:message:v1">
  <Header>
    <MessageId>...</MessageId>
    <MessageType>SubmitOrderRequest</MessageType>
    <SchemaVersion>1.0</SchemaVersion>
  </Header>
  <Body>
    <ord:SubmitOrderRequest xmlns:ord="urn:commerce:order:v1">
      ...
    </ord:SubmitOrderRequest>
  </Body>
</Envelope>

This separates transport envelope from business document.


15. Schema Design Styles

There are common XSD design styles.

15.1 Russian Doll

Nested local definitions.

<xs:element name="Order">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="OrderId" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Best for small one-off schemas.

15.2 Salami Slice

Global elements referenced locally.

<xs:element name="OrderId" type="xs:string"/>
<xs:element name="CustomerId" type="xs:string"/>

<xs:complexType name="OrderType">
  <xs:sequence>
    <xs:element ref="OrderId"/>
    <xs:element ref="CustomerId"/>
  </xs:sequence>
</xs:complexType>

Reusable but can produce many global elements.

15.3 Venetian Blind

Global named types, local elements.

<xs:complexType name="OrderType">
  <xs:sequence>
    <xs:element name="OrderId" type="OrderIdType"/>
    <xs:element name="CustomerId" type="CustomerIdType"/>
  </xs:sequence>
</xs:complexType>

Often a good enterprise default.

Recommendation:

Use Venetian Blind for stable enterprise contracts: global root elements, named reusable types, local business elements.

16. Example: Production-Grade Order XSD Skeleton

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="urn:commerce:order:v1"
    xmlns="urn:commerce:order:v1"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">

  <xs:element name="SubmitOrderRequest" type="SubmitOrderRequestType"/>
  <xs:element name="SubmitOrderResponse" type="SubmitOrderResponseType"/>

  <xs:complexType name="SubmitOrderRequestType">
    <xs:sequence>
      <xs:element name="RequestId" type="RequestIdType"/>
      <xs:element name="SubmittedAt" type="xs:dateTime"/>
      <xs:element name="Customer" type="CustomerRefType"/>
      <xs:element name="Order" type="OrderType"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="CustomerRefType">
    <xs:sequence>
      <xs:element name="CustomerId" type="CustomerIdType"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="OrderType">
    <xs:sequence>
      <xs:element name="OrderId" type="OrderIdType" minOccurs="0"/>
      <xs:element name="Currency" type="CurrencyCodeType"/>
      <xs:element name="LineItems" type="LineItemsType"/>
      <xs:element name="Notes" type="NotesType" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="LineItemsType">
    <xs:sequence>
      <xs:element name="LineItem" type="LineItemType" minOccurs="1" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="LineItemType">
    <xs:sequence>
      <xs:element name="LineNumber" type="xs:positiveInteger"/>
      <xs:element name="Sku" type="SkuType"/>
      <xs:element name="Quantity" type="QuantityType"/>
      <xs:element name="UnitPrice" type="xs:decimal"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="SubmitOrderResponseType">
    <xs:sequence>
      <xs:element name="RequestId" type="RequestIdType"/>
      <xs:element name="Accepted" type="xs:boolean"/>
      <xs:element name="OrderId" type="OrderIdType" minOccurs="0"/>
      <xs:element name="Errors" type="ErrorsType" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="ErrorsType">
    <xs:sequence>
      <xs:element name="Error" type="ErrorType" minOccurs="1" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="ErrorType">
    <xs:sequence>
      <xs:element name="Code" type="xs:string"/>
      <xs:element name="Message" type="xs:string"/>
      <xs:element name="Field" type="xs:string" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>

  <xs:simpleType name="RequestIdType">
    <xs:restriction base="xs:string">
      <xs:minLength value="1"/>
      <xs:maxLength value="64"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="CustomerIdType">
    <xs:restriction base="xs:string">
      <xs:minLength value="1"/>
      <xs:maxLength value="64"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="OrderIdType">
    <xs:restriction base="xs:string">
      <xs:minLength value="1"/>
      <xs:maxLength value="64"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="SkuType">
    <xs:restriction base="xs:string">
      <xs:minLength value="1"/>
      <xs:maxLength value="80"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="QuantityType">
    <xs:restriction base="xs:positiveInteger">
      <xs:maxInclusive value="999999"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="CurrencyCodeType">
    <xs:restriction base="xs:string">
      <xs:pattern value="[A-Z]{3}"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="NotesType">
    <xs:restriction base="xs:string">
      <xs:maxLength value="2000"/>
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

This schema is not perfect, but it demonstrates important production habits:

  • explicit namespace;
  • explicit roots;
  • named business types;
  • bounded strings;
  • bounded quantity;
  • required line items;
  • separate request/response roots;
  • structured errors.

17. Structural Contract vs Business Contract

XSD should not absorb all business logic.

Example business rule:

If Accepted is true, OrderId must exist and Errors must not exist.
If Accepted is false, Errors must exist and OrderId must not exist.

Can XSD model this? In XSD 1.0, awkwardly. In XSD 1.1, assertions can help. But in many Java production systems, cross-field conditional logic is better handled in domain validator.

Layering:

Example:

RuleBest Layer
OrderId is string max 64XSD
LineItem at least oneXSD
Currency is 3 uppercase lettersXSD simple type
OrderTotal = sum(line totals)Domain validator
Customer exists and activeDomain/service validator
Order can be submitted in current lifecycle stateWorkflow/state machine

18. Designing For Compatibility

Compatibility is usually more important than elegance.

18.1 Safe Changes

Usually safer:

  • adding optional element at end of sequence;
  • adding optional attribute;
  • relaxing max length carefully;
  • adding new enum only if consumers tolerate unknown values;
  • adding extension point with clear namespace policy.

18.2 Breaking Changes

Usually breaking:

  • removing required element;
  • making optional element required;
  • changing namespace unexpectedly;
  • changing element order;
  • renaming element;
  • changing datatype from string to decimal;
  • tightening max length;
  • removing enum value;
  • changing meaning without changing structure.

18.3 Sequence Compatibility Trap

Given:

<xs:sequence>
  <xs:element name="A" type="xs:string"/>
  <xs:element name="B" type="xs:string"/>
</xs:sequence>

Adding optional C between A and B:

<xs:sequence>
  <xs:element name="A" type="xs:string"/>
  <xs:element name="C" type="xs:string" minOccurs="0"/>
  <xs:element name="B" type="xs:string"/>
</xs:sequence>

may break strict consumers or generated bindings expecting old order.

Safer:

<xs:sequence>
  <xs:element name="A" type="xs:string"/>
  <xs:element name="B" type="xs:string"/>
  <xs:element name="C" type="xs:string" minOccurs="0"/>
</xs:sequence>

Contract evolution should be planned from day one.


19. Extension Points

Extension points allow future data without breaking old contracts.

19.1 xs:any

<xs:complexType name="OrderType">
  <xs:sequence>
    <xs:element name="OrderId" type="OrderIdType"/>
    <xs:element name="CustomerId" type="CustomerIdType"/>
    <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

Meaning:

  • namespace="##other": only elements from other namespaces;
  • processContents="lax": validate if schema is known, otherwise allow;
  • repeated extension elements allowed.

19.2 Extension Point Risks

Risks:

  • hidden coupling;
  • payload bloat;
  • inconsistent partner data;
  • unclear ownership;
  • XPath brittle if extension content becomes business-critical.

Policy:

Extension points are for non-critical optional extension, not for avoiding contract governance.

20. Identity Constraints

XSD supports identity constraints such as xs:unique, xs:key, and xs:keyref.

Example: line number unique inside order.

<xs:element name="Order" type="OrderType">
  <xs:unique name="uniqueLineNumber">
    <xs:selector xpath="LineItems/LineItem"/>
    <xs:field xpath="LineNumber"/>
  </xs:unique>
</xs:element>

Use carefully.

Pros:

  • catches structural consistency errors early;
  • useful for document-local uniqueness;
  • makes contract more expressive.

Cons:

  • XPath subset limitations;
  • namespace complexity;
  • error messages can be hard for partners;
  • generated tooling support varies;
  • not a replacement for database/domain identity.

Production guideline:

Use XSD identity constraints for document-local consistency, not system-of-record identity.


21. XSD 1.0 vs XSD 1.1 Awareness

Many Java stacks historically support XSD 1.0 by default through JAXP/Xerces-like providers. XSD 1.1 adds features such as assertions, but support depends on processor/provider.

Practical rule:

Do not design a contract that requires XSD 1.1 unless every producer, consumer, validator, and toolchain explicitly supports it.

If you need conditional logic and your ecosystem is XSD 1.0-centric, implement it in semantic validation layer.


22. Java Validation Preview

Detailed Java validation comes in part 013, but understand the shape now.

SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new StreamSource(xsdInputStream));
Validator validator = schema.newValidator();
validator.validate(new StreamSource(xmlInputStream));

Important lifecycle:

ObjectLifecycle Recommendation
SchemaFactoryConfigure centrally; not your main cache unit
SchemaImmutable grammar representation; good candidate for cache
ValidatorCreate per validation use; not thread-safe in practice
SourcePer input
ErrorHandlerPer validation, bounded collector

Do not compile schema on every request unless throughput is irrelevant.


23. Schema Documentation

XSD supports annotations.

<xs:element name="OrderId" type="OrderIdType">
  <xs:annotation>
    <xs:documentation>
      Stable identifier assigned by order management system. Required after order acceptance.
    </xs:documentation>
  </xs:annotation>
</xs:element>

Documentation should clarify semantics that XSD cannot express:

  • field meaning;
  • lifecycle condition;
  • source of truth;
  • whether empty string is allowed semantically;
  • whether value is case-sensitive;
  • version compatibility notes;
  • deprecation plan.

Contract docs are not optional for partner-facing XML.


24. Schema Review Checklist

Before approving an XSD, review:

Structure

  • Root elements are explicit.
  • Namespace is stable and intentional.
  • elementFormDefault is chosen deliberately.
  • Required vs optional fields are justified.
  • Lists have correct minOccurs.
  • maxOccurs="unbounded" is intentional.
  • Element ordering is stable.

Types

  • Important business identifiers have named types.
  • Strings have max length where appropriate.
  • Numeric types fit domain precision.
  • Date/time types have timezone semantics documented.
  • Enumerations have evolution policy.

Compatibility

  • New fields can be added safely.
  • Consumers can ignore unknown extension content if needed.
  • Breaking changes require new namespace/version.
  • Generated binding impact is considered.

Operations

  • Schema can be loaded without internet.
  • Imports/includes resolve through approved catalog.
  • Validation error messages are understandable.
  • Test fixtures cover valid and invalid payloads.

25. Common XSD Anti-Patterns

25.1 Everything Is xs:string

Bad:

<xs:element name="Quantity" type="xs:string"/>
<xs:element name="Price" type="xs:string"/>
<xs:element name="SubmittedAt" type="xs:string"/>

Better:

<xs:element name="Quantity" type="xs:positiveInteger"/>
<xs:element name="Price" type="xs:decimal"/>
<xs:element name="SubmittedAt" type="xs:dateTime"/>

Use proper types, then apply business validation where needed.

25.2 Over-Constraining Too Early

Bad:

<xs:simpleType name="CustomerNameType">
  <xs:restriction base="xs:string">
    <xs:pattern value="[A-Z][a-z]+ [A-Z][a-z]+"/>
  </xs:restriction>
</xs:simpleType>

Names are culturally complex. This pattern will reject valid real-world names.

Better:

<xs:simpleType name="CustomerNameType">
  <xs:restriction base="xs:string">
    <xs:minLength value="1"/>
    <xs:maxLength value="200"/>
  </xs:restriction>
</xs:simpleType>

25.3 Unbounded Everything

Bad:

<xs:element name="Attachment" type="AttachmentType" maxOccurs="unbounded"/>

Better:

<xs:element name="Attachment" type="AttachmentType" minOccurs="0" maxOccurs="20"/>

Even if technical maximum differs from business maximum, the contract should communicate realistic constraints where possible.

25.4 Namespace Version Chaos

Bad:

targetNamespace="urn:order"

then silently change meaning over time.

Better:

targetNamespace="urn:commerce:order:v1"

and define compatibility rules for minor changes.

25.5 Schema As Database Dump

Bad XSD mirrors tables:

<TBL_ORDER>
  <ORDER_PK>...</ORDER_PK>
  <CUST_FK>...</CUST_FK>
</TBL_ORDER>

Better XSD models document intent:

<SubmitOrderRequest>
  <Customer>
    <CustomerId>...</CustomerId>
  </Customer>
  <Order>...</Order>
</SubmitOrderRequest>

Contracts should model business messages, not persistence internals.


26. Capstone Mini Exercise

Design an XSD for CancelOrderRequest.

Requirements:

  • namespace: urn:commerce:order:v1;
  • root: CancelOrderRequest;
  • fields:
    • RequestId, required, string 1–64;
    • OrderId, required, string 1–64;
    • RequestedAt, required, xs:dateTime;
    • RequestedBy, required, string 1–120;
    • Reason, optional, string max 1000;
  • include optional extension point from other namespaces;
  • use named types.

Expected shape:

<CancelOrderRequest xmlns="urn:commerce:order:v1">
  <RequestId>REQ-100</RequestId>
  <OrderId>ORD-900</OrderId>
  <RequestedAt>2026-07-02T10:15:30Z</RequestedAt>
  <RequestedBy>ops-user-1</RequestedBy>
  <Reason>Customer requested cancellation.</Reason>
</CancelOrderRequest>

Self-check:

  • Can you explain which rules are enforced by XSD?
  • Which rules still need domain validation?
  • What future field could be added without breaking old consumers?
  • Would you allow Reason to be empty? Why?
  • Should RequestedBy be a user ID, display name, or actor reference type?

27. Ringkasan

XSD adalah contract language untuk XML.

Mental model utama:

  • XSD defines grammar, not full business truth.
  • Elements appear in XML; types define allowed structure/value.
  • Namespace is part of contract identity.
  • sequence, choice, all, minOccurs, and maxOccurs define document shape.
  • Optional, nil, and empty are semantically different.
  • Named types improve reuse and governance.
  • Compatibility must be designed before version 1 is published.
  • Use XSD to reject structurally invalid documents early, then use domain validation for business semantics.

Part berikutnya akan membahas XSD types, datatypes, facets, value constraints, regex, date/time, decimal precision, enum strategy, ID/IDREF, nil/default semantics, dan validation edge cases secara lebih dalam.


References

  • W3C XML Schema Part 1: Structures.
  • W3C XML Schema Definition Language 1.1 Part 1: Structures.
  • Java javax.xml.validation package documentation.
  • Oracle Java API for XML Processing documentation.
Lesson Recap

You just completed lesson 10 in build core. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.