In Trellis, every service is defined by a contract. The contract is the authoritative description of what your service does: what databases it needs, what RPCs it handles, what events it emits or consumes, and who is allowed to call it. Trellis compares the contract boundary with deployment authority, records any required authority plan, and reconciles accepted resource changes before granting runtime access.
Before writing a contract, you may want to read Contracts for the contract model and identity rules, and Architecture for the platform vs domain boundary.
Start with the smallest possible service contract:
Let’s start with the smallest possible contract. Create contracts/orders_service.ts:
import { defineServiceContract } from "@qlever-llc/trellis";
export const ordersService = defineServiceContract({}, () => ({
id: "orders-service@v1",
displayName: "Orders Service",
description: "A simple service for managing orders.",
uses: {},
resources: {},
rpc: {},
events: {},
}));
export default ordersService; This is a valid, if empty, contract. We will fill it in as we add capabilities. Export the contract object itself and let Trellis use that object for runtime bootstrap, API typing, and SDK generation.
Also export short local type aliases for handler modules. The service should use contract-derived types directly instead of consuming its own generated SDK:
import type {
EventHandler,
EventName,
RpcArgs,
RpcMethodNameOf,
RpcResult,
} from "@qlever-llc/trellis";
export type RpcHandlerArgs<T extends RpcMethodNameOf<typeof ordersService>> =
RpcArgs<typeof ordersService, T>;
export type RpcHandlerResult<T extends RpcMethodNameOf<typeof ordersService>> =
RpcResult<typeof ordersService, T>;
export type Event<T extends EventName<typeof ordersService>> =
EventHandler<typeof ordersService, T>; We will use those aliases for every handler in this tutorial.