项目文件夹

文件
Asim Aslam c7657f73f4
goreleaser / goreleaser (push) Has been cancelled
Refactor agent plan storage, update docs, and release v6 (#2977)
* test(harness): read agent plan from the scoped store

The store-scoping change moved an agent's plan from the default table
key agent/{name}/plan to its own table (database "agent", table {name},
key "plan"). The plan-delegate harness tests still read the old key and
failed with 'not found'; read through store.Scope(mem, "agent", name)
like the agent does.

* docs: orient agents-first across README, landing, and docs overview

Lead with agents (then services and flows), surface MCP + A2A as the
interop story, and frame agents as services. Landing hero and feature
grid reordered agents-first with an A2A gateway card.

* v6: module path go-micro.dev/v6, TLS secure by default, NewService

Cut v6. Three breaking changes, bundled so the major bump is paid once:

- Module path go-micro.dev/v5 -> go-micro.dev/v6 across all imports + go.mod.
- TLS verification on by default (was off). MICRO_TLS_SECURE removed;
  MICRO_TLS_INSECURE=true opts out for self-signed/dev.
- micro.NewService(name, opts...) is the canonical service constructor,
  symmetric with NewAgent/NewFlow; micro.New kept as a deprecated alias;
  the old name-less NewService(opts...) removed. Generators emit NewService.

Also ports the JWT auth token provider in-module (go-micro.dev/v6/auth/jwt/token
on golang-jwt/jwt/v5), dropping the v5-pinned github.com/micro/plugins/v5/auth/jwt
and the deprecated dgrijalva/jwt-go.

Docs/README/landing updated to v6 and @latest; v5->v6 migration guide added;
CHANGELOG cut as [6.0.0]. Blog posts left at their historical versions.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-18 11:55:35 +01:00

4.3 KiB

layout, title, permalink, description
layout title permalink description
blog Developer Experience Cleanup: One Way to Do Things /blog/5 Unified service creation, cleaner handler registration, and modular monolith support — the Go Micro DX overhaul

Developer Experience Cleanup: One Way to Do Things

Developer Experience Cleanup: One Way to Do Things

March 4, 2026 — By the Go Micro Team

Go Micro has always prioritized getting out of your way. But over time, the API accumulated multiple ways to do the same thing — micro.NewService(), micro.NewService(), service.New(), three different handler registration patterns. If you're building something for AI agents or running a modular monolith, you shouldn't have to choose between equivalent APIs.

We've cleaned it up. Here's what changed and why.

One Way to Create a Service

Before, there were three ways to create a service:

// Old: three equivalent patterns
service := micro.NewService("greeter")                        // name only
service := micro.NewService(micro.Name("greeter"))     // options only
service := service.New(service.Name("greeter"))        // internal package

Now there's one canonical pattern:

service := micro.NewService("greeter")
service := micro.NewService("greeter", micro.Address(":8080"))

Name is always the first argument. Options follow. NewService still works (it's deprecated, not removed), but every example, doc, and guide now uses micro.NewService().

Clean Handler Registration

Registering handlers used to require reaching through to the server:

// Old: verbose, leaks abstraction
handler := service.Server().NewHandler(
    &TaskService{tasks: make(map[string]*Task)},
    server.WithEndpointScopes("TaskService.Create", "tasks:write"),
)
service.Server().Handle(handler)

Now service.Handle() accepts handler options directly:

// New: clean, one call
service.Handle(
    &TaskService{tasks: make(map[string]*Task)},
    server.WithEndpointScopes("TaskService.Create", "tasks:write"),
)

For the common case with no options, it's just:

service.Handle(new(Greeter))

Modular Monoliths with Service Groups

Run multiple services in a single binary. Each service gets isolated state (server, client, store, cache) while sharing infrastructure (registry, broker, transport):

users := micro.NewService("users", micro.Address(":9001"))
orders := micro.NewService("orders", micro.Address(":9002"))

users.Handle(new(Users))
orders.Handle(new(Orders))

g := micro.NewGroup(users, orders)
g.Run()

Start as a monolith, split into separate binaries when you need independent scaling. The Group handles signals and coordinated shutdown — all services start together and stop together.

MCP Integration in One Line

Every service is automatically an MCP tool. Add a gateway alongside your service with one option:

service := micro.NewService("greeter",
    micro.Address(":9090"),
    mcp.WithMCP(":3000"),
)

service.Handle(new(Greeter))
service.Run()

Your Go comments become tool descriptions. Your struct tags become parameter schemas. No glue code.

Bug Fixes

  • Stop() error handling: Previously, Stop() would silently swallow errors from BeforeStop hooks. Now all errors are properly propagated.
  • Store initialization: Fatal-level log on store init failure changed to error-level — a store init failure shouldn't crash your service.
  • Service interface: The internal implementation is now properly unexported. Users interact through the service.Service interface, not a concrete type.

What This Means for You

If you're building new services, use micro.NewService("name", opts...) and service.Handle(). That's it.

If you have existing code using micro.NewService() or service.Server().Handle(), everything still works — we didn't break anything. But the docs, examples, and guides all point to the new patterns now.

The goal is simple: when someone asks "how do I create a service?", there should be exactly one answer.

See the updated Getting Started guide and the agent demo for working examples.