Building a SOAP service: contract-first and code-first

Generating the contract from code is faster to start and harder to govern; writing the WSDL first gives you a reviewable interface.

Choosing the direction

Contract-firstCode-first
Starting pointA hand-written WSDL plus XSDAn annotated class
Who can review the interfaceAnyone, including partnersOnly people who read the language
Namespace and type controlFullWhatever the tool generates
Speed to first endpointSlowerFast
Versioning disciplineNaturalRetrofitted
Best forPartner-facing, long-lived contractsInternal services, prototypes
# Contract-first with JAX-WS: generate the portable artifacts, then implement
wsimport -keep -p com.example.orders -d build/classes contract.wsdl

# .NET: generate a client or a server-side abstract class
dotnet tool install --global dotnet-svcutil
dotnet-svcutil contract.wsdl --outputDir Generated

# Java: publish a contract-first endpoint without a container
# (endpoint is the portable JAX-WS API)
#   Endpoint.publish("http://0.0.0.0:8080/orders", new OrderServiceImpl());
# then check it
curl -s "http://localhost:8080/orders?wsdl" | head -20
// Code-first with WCF-style attributes, still common inside enterprises
[ServiceContract(Namespace = "urn:example:orders")]
public interface IOrderService
{
    [OperationContract]
    [FaultContract(typeof(OrderFault))]
    OrderResult PlaceOrder(Order order);
}

[DataContract(Namespace = "urn:example:orders")]
public class Order
{
    [DataMember(Order = 1, IsRequired = true)] public string Sku { get; set; } = "";
    [DataMember(Order = 2)] public int Qty { get; set; } = 1;
    [DataMember(Order = 3, EmitDefaultValue = false)] public string? Note { get; set; }
}
  • Document/literal wrapped is the interoperable style — use it unless a partner forces RPC/encoded.
  • Pin the namespace to a stable URI you own. Changing it is a breaking change for every consumer.
  • Annotate operations so their element names are explicit, otherwise the generated WSDL changes when someone renames a method parameter.
  • Expose the WSDL from the running service so it can never drift from the code, and keep a copy under version control for review.
  • Version an endpoint by adding a new service element rather than editing the old one in place.
💡
The namespace in a SOAP contract is an identifier, not a URL that anyone fetches. Pick a URI-shaped string you will never need to change, because changing it later invalidates every generated client on both sides.

FAQ

Is RPC/encoded still acceptable?
Only when a partner's toolkit requires it. It violates the WS-I Basic Profile, encodes types in a way that breaks across vendors, and no modern framework defaults to it.
How do I add a field without breaking clients?
Add it as optional at the end of a sequence. Old clients ignore unknown elements only if they are lenient; schema validation on the client side is where the breakage appears.

WSDL contracts and code generation SOAP in the enterprise: ESBs, gateways and middleware

Last refreshed 2026-09-18.