Interfaces
🚧 This page is under active renovation for wasmCloud 1.0. Some details may not be aligned with v1.0 during the alpha period. 🚧
Overview​
Interface-driven development (IDD), aka contract-driven development, is a development approach that focuses on defining what components need before how to meet those needs.
Systems developed using IDD—especially distributed systems—are more loosely coupled, robust and maintainable.
By default, WebAssembly components operate in a completely isolated sandbox, meaning that they can only perform logical operations with no access to system resources like I/O, networking, and syscalls.
In functional programming terminology we might call these modules "pure", as they can only map inputs to outputs without producing side-effects.
But without any side-effects, how can we use WebAssembly components to do anything useful in our applications?
Interface-driven development is the core that makes wasmCloud possible.
Separate Functional and Non-Functional Requirements​
wasmCloud embraces interface-driven development and separation of concerns in its use of components and providers. Components implement the functional requirements of (part of) an application: compute a result, allow access, respond to requests, etc. Capability providers implement the non-functional requirements, such as connecting to a data store, serving HTTP requests, or sending a message to a queue. Components and providers communicate through interfaces called capabilities.
WebAssembly Interface Type (WIT)​
wasmCloud uses WIT as its Interface Definition Language (IDL) for interfaces.
WIT is designed to allow WebAssembly components to define the interfaces they support ("exports") and the capabilities they need ("imports"). wasmCloud also uses WIT to define the interface/contract provided by capability providers.
As of v1.0, wasmCloud has migrated interfaces from Smithy to WIT. If you see references to Smithy in the wasmCloud ecosytem, they are referring to the stable ABI
Example: A greeter
interface defined in WIT​
WIT interfaces are developed in the open and are simple to understand and read:
package local:greeter-demo; // <namespace>:<package>
interface greet { // interface <name of interface>
greet: func(name: string) -> string; // a function named "greet"
}
world greeter {
export greet; // make the `greet` function available to other components/the runtime
}
While reading the spec is the best way to learn about WIT, it is also reasonably simple to understand at a glance.
How to use WIT in your WebAssembly components is beyond the scope of this document, but wit-bindgen
is a great place to start if you are interested.
Compare: WIT vs. gRPC​
For those familiar with gRPC, the above WIT interface would look something like the following:
syntax = "proto3";
package greeter;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Similar to gRPC, WIT defines both a schema and a method for serialization/movement across abstraction boundaries (via the Component Model).
A key difference: while gRPC is meant to perform over network boundaries, WIT is in-process, and performs at near-native speed.
Compare: WIT vs. Smithy​
For those familiar with smithy, the above WIT interface would look something like the following:
namespace com.example.greeter
service GreeterService {
version: "2022-01-01",
operations: [Greet]
}
operation Greet {
input: GreetRequest,
output: GreetResponse
}
structure GreetRequest {
@required
name: String
}
structure GreetResponse {
// Required string field
@required
message: String
}
Similar to Smithy, WIT defines both a schema and a method for serialization/movement across abstraction boundaries (via the Component Model).
A key difference: while Smithy is meant to perform over network boundaries, WIT is in-process, and performs at near-native speed.