Single-Source definition.go, One Generator
v1.41.0 collapses each microservice's API down to a single, typed, hand-written source of truth: *api/definition.go. It declares the service's Hostname, metadata consts, and one define.* var per feature alongside its input/output structs — and everything else is generated from it. The new cmd/genservice tool reads definition.go and emits client.go, intermediate.go, mock.go, mock_test.go, and manifest.yaml in one pass, replacing the separate genmanifest and genmock tools (both deleted). The old hand-maintained endpoints.go is gone. The win is the end of drift: the five files that previously had to be kept in sync — by hand, by two generators, across every edit — now derive deterministically from one declarative spec, and each generated file carries a DO NOT EDIT header. Because the spec is typed Go, the compiler checks it: a feature's In/Out types must exist, and a cross-service InboundEvent.Source reference is verified across package boundaries, so a removed or renamed event becomes a compile error in every consumer. There is no change to the runtime or to the generated client API — service-to-service callers (NewClient, URL(), …) are untouched. This is an authoring-model and code-generation change, and the upgrade skill performs the migration mechanically.
Highlights
*api/definition.gois the single source of truth (new layout). One hand-written, typed file per microservice declares theHostname,Name,Version, andDescriptionconsts plus onedefine.*var per feature with its In/Out structs. It replacesendpoints.go.cmd/genserviceis the one generator. Fromdefinition.goit emitsclient.go,intermediate.go,mock.go,mock_test.go, andmanifest.yaml. A-checkflag lets CI fail on stale generated files.genmanifestandgenmockare deleted. Their jobs fold intogenservice. Housekeeping now runs one tool instead of two.- The
definepackage gives the spec a typed vocabulary.define.Function,define.Web,define.Task,define.Workflow,define.OutboundEvent,define.InboundEvent,define.Config,define.Metric, anddefine.Ticker— bare nouns so the spec reads as the contract. manifest.yamlis now a derived projection. It is generated fromdefinition.goand never hand-authored. You still read manifests to map the system; you no longer write them.- Compile-time cross-service safety.
InboundEvent.Sourceis a typed reference to the source service's event var, so a removed or renamed event is caught by the compiler in every sink, not at runtime. - Markers in generated files. A uniform
// MARKER: Featureanchor now appears indefinition.go,intermediate.go,client.go,mock.go, andmock_test.go, so "show me everything for feature X" is one grep within the microservice.
New Features
The definition.go Spec
A microservice's API is now authored as a small declarative file. Each feature is a define.* var whose godoc is the feature's description; its input and output structs sit beside it, with per-field documentation in jsonschema_description:"..." tags:
package calculatorapi
import "github.com/microbus-io/fabric/define"
// Hostname is the default hostname of the microservice.
const Hostname = "calculator.example"
// Name is the decorative PascalCase name of the microservice.
const Name = "Calculator"
// Description is the human-readable summary of the microservice.
const Description = `The Calculator microservice performs simple mathematical operations.`
// Arithmetic performs an arithmetic operation between two integers x and y given an operator op.
var Arithmetic = define.Function{ // MARKER: Arithmetic
Host: Hostname, Method: "GET", Route: ":443/arithmetic",
In: ArithmeticIn{}, Out: ArithmeticOut{},
}
// ArithmeticIn are the input arguments of Arithmetic.
type ArithmeticIn struct { // MARKER: Arithmetic
X int `json:"x,omitzero" jsonschema_description:"X is the left operand"`
Op string `json:"op,omitzero" jsonschema_description:"Op is the operator: + - * /"`
Y int `json:"y,omitzero" jsonschema_description:"Y is the right operand"`
}Field values are literals only — constant values, struct composite literals as type carriers (ArithmeticIn{}), and references to other define.* vars — so the whole spec is statically resolvable and the compiler verifies it. The routing fields (Host, Method, Route, RequiredClaims, TimeBudget, LoadBalancing) live directly on each routable kind, and Arithmetic.URL() still resolves the canonical endpoint URL for LLM tools and graph/task references, exactly as before.
One Generator: genservice
Run genservice on a microservice directory after editing its definition.go:
go run github.com/microbus-io/fabric/cmd/genservice ./calculatorIt regenerates all five derived artifacts together — the client.go stubs (Client, MulticastClient, MulticastTrigger, Hook, Executor, Subflow), the intermediate.go wiring, mock.go, mock_test.go, and manifest.yaml — so they can never fall out of sync. The housekeeping skill invokes it automatically; you rarely call it by hand. genservice -check regenerates in memory and exits non-zero if anything on disk is stale, a drop-in CI guard against a forgotten regeneration.
Typed Configs, Metrics, and Cross-Service Events
The typed spec captures detail that previously lived only in hand-written recorder methods or scattered files:
- Struct/JSON-valued configs are now fully generated. A
define.ConfigwhoseValuecarrier is a struct produces a typed getter that JSON-unmarshals the stored value and a setter that marshals it — no hand-written accessor. - Metrics declare their value type and labels (
define.Metric{Kind: define.Counter, Value: int(0), Labels: []string{"op"}}), so theIncrement/Recordrecorder methods are generated rather than hand-rolled. - Manual-subscription knobs (
Manual,Tags) are declared on the feature'sdefine.*var and flow through to the generated subscription wiring andHook.
Breaking Changes
The upgrade skill handles each of these mechanically. Manual migration is not recommended.
endpoints.gois replaced bydefinition.go(loud, layout change). Every microservice's hand-maintainedendpoints.gois removed and a typeddefinition.gosynthesized in its place. The In/Out and domain-type declarations are lifted verbatim, preserving tags and comments.genmanifestandgenmockare deleted (loud if invoked). Replace any direct invocation, script, or CI step withgenservice. Housekeeping already runs the new tool.- The authoring target moves to
definition.go(workflow change). Add, modify, and remove features by editingdefinition.goand regenerating. The generatedclient.go,intermediate.go,mock.go,mock_test.go, andmanifest.yamlcarry aDO NOT EDITheader and must not be hand-edited;manifest.yamlis now a read-only derived view. - The
Input:/Output:godoc convention is retired; descriptions standardize on thejsonschema_descriptiontag (loud for OpenAPI/LLM-tool descriptions). Per-argument and per-field descriptions now come solely fromjsonschema_description:"..."tags on the feature's In/Out struct fields indefinition.go; the endpoint's overall description is thedefine.*var's godoc. The old structuredInput:/Output:godoc sections on the handler are no longer parsed, so any per-argument text that lived there must move to the struct-field tags to keep appearing in the OpenAPI document and LLM tool schemas. Authoring also switches from thejsonschema:"description=..."subtag to the dedicatedjsonschema_description:"..."tag, which is read whole — so a description may now contain commas (thejsonschematag is comma-split into directives and silently truncated a description at its first comma). Thejsonschema:"..."tag is reserved for other directives such asjsonschema:"example=6". A scalar query or path argument is now describable by the same tag as a body field, closing a gap whereInput:/Output:had been the only way to document one. The upgrade migration moves existing descriptions ontojsonschema_descriptionverbatim. - No runtime or client-API change. Service-to-service call sites,
NewClient/NewMulticastClient/NewHook, andSomeFeature.URL()are unchanged. Nothing downstream of a migrated service needs adapting.
Migration
From inside a Microbus project, ask Claude Code to upgrade Microbus:
Get the latest version of Microbus.
The upgrade skill drives the new versioned migration tool, cmd/genupgrade -v 1.41.0, per microservice:
- Bump
go.modtov1.41.0andgo mod tidy. - Refresh
.claude/rules/and.claude/skills/and other framework-managed files. - For each microservice, run
genupgrade -v 1.41.0to synthesize*api/definition.gofrom itsmanifest.yaml,endpoints.go(In/Out and domain types), andintermediate.go, then deleteendpoints.go. Sources are migrated before their event sinks soInboundEvent.Sourcereferences resolve. - Run
genserviceon each microservice to regenerateclient.go,intermediate.go,mock.go,mock_test.go, andmanifest.yaml. - Run
go vet ./...and the integration test suite, and review the diff for substantive correctness.
service.go handler bodies and OnStartup/OnShutdown are never touched by the migration. genupgrade is a permanent, versioned tool — downstream projects bumping to v1.41.0 later run the same routine.
Documentation
- Updated: Uniform Code Structure for the
definition.go+genservicelayout. - Updated:
cmdwith thegenservicereference, replacinggenmanifestandgenmock. - Updated: My First Microservice, OpenAPI-Delegating Microservices, and Python-Backed Microservices scaffold layouts.
- Updated: Package Reference codegen-CLI listing.
- Updated: LLM Tooling and Building an LLM Workflow — per-argument descriptions via
jsonschema_descriptionfield tags, replacing the retiredInput:/Output:godoc convention.