This guide is the cookbook for porting an existing Firefly Java service (or a sibling .NET service) to the Go port. Each section maps a Java / .NET concept to its idiomatic Go translation.
Java → Go
org.fireflyframework:firefly-common → github.com/.../kernel
firefly-common-utils → utils
firefly-common-validators → validators
firefly-web → web
firefly-common-cache → cache
firefly-otel-spring-boot-starter → observability
firefly-common-data → data
firefly-common-cqrs → cqrs
firefly-common-eda → eda
firefly-event-sourcing-… → eventsourcing
firefly-common-domain (orchestration)→ orchestration
firefly-common-rule-engine → ruleengine
firefly-platform-plugins → plugins
firefly-service-client → client
firefly-config-server → configserver
firefly-idp + firefly-idp-* → idp / idpinternaldb / idpkeycloak / idpazuread / idpawscognito
firefly-ecm + firefly-ecm-* → ecm / ecm{storage,esignature}*
firefly-notifications + … → notifications / notifications{sendgrid,resend,twilio,firebase}
firefly-callbacks → callbacks
firefly-webhooks → webhooks
spring-boot @ConfigurationProperties → config
spring-boot SpringApplication → lifecycle
spring-boot-starter-actuator → actuator
spring @Scheduled → scheduling
resilience4j-* → resilience
spring-security → security
flyway → migrations
springdoc-openapi → openapi
spring MessageSource → i18n
spring ServerSentEvent → sse
spring @Transactional → transactional
spring-boot-starter-test → testkit
The following modules close the gap with the Spring Boot stack and the PyFly sibling. Each is wire-shape compatible with its Java / .NET counterpart but expressed in idiomatic Go (constructor injection, explicit middleware composition).
| Spring concept | Go module + entry point |
|---|---|
@ConfigurationProperties + YAML bind |
config.Load[T](ctx, sources...) / config.LoadFromProfile[T](...) |
SpringApplication.run() |
lifecycle.New(name).OnStart(...).OnHTTP(":8080", mux).Run(ctx) |
spring-boot-starter-actuator |
actuator.Mount(actuator.Config{...}) or core.ActuatorHandler(...) |
@Scheduled(cron="0 9 * * MON-FRI") |
scheduler.Cron("name", "0 9 * * 1-5", run) / FixedRate(...) / FixedDelay(...) |
@CircuitBreaker @RateLimiter @Bulkhead |
resilience.Chain(timeout, breaker, bulkhead).Execute(ctx, fn) |
@PreAuthorize("hasRole('ADMIN')") |
security.NewFilterChain().Require("/admin/", "ADMIN").Middleware() |
Flyway V001__init.sql |
migrations.Run(ctx, db, migrations.NewFSSource(fsys, "db")) |
@RestController + springdoc |
openapi.New(info).Add(RouteDef{...}).Handler() |
MessageSource.getMessage(...) |
bundle.T(locale, key, args) after i18n.LocaleMiddleware(bundle) |
Spring ServerSentEvent |
w, _ := sse.NewWriter(rw, r, pingInterval); w.Send(sse.Event{...}) |
@Transactional |
transactional.WithTx(ctx, db, func(ctx) error { ... }) |
@SpringBootTest |
httptest.NewServer(BuildHandler()) + testkit.{SignHMAC,SpyBroker,MustEncode} |
The Java framework uses Project Reactor; the .NET port uses
Task/IAsyncEnumerable. Idiomatic Go does not have reactive streams.
| Java | Go |
|---|---|
Mono<T> |
(T, error) with leading ctx context.Context |
Flux<T> |
chan T or Go 1.23 iter.Seq[T] |
Mono.error(new ResourceNotFound()) |
return zero, kernel.NewNotFound("…") |
Mono.deferContextual(…) |
Read from context.Context |
Schedulers / subscribeOn |
Goroutines + go fn() |
Mono.timeout(...) |
context.WithTimeout(ctx, d) |
Every Firefly Go method takes a leading ctx context.Context and
respects its cancellation.
| Java | Go |
|---|---|
throw new ResourceNotFoundException(...) |
return kernel.NewNotFound("…") |
@ControllerAdvice ProblemDetail handler |
web.ProblemMiddleware + web.ErrorHandler |
ErrorEnvelope |
kernel.ProblemDetail |
OperationResult<T> |
kernel.Result[T] (or just (T, error)) |
// Java
@CommandHandler
public Mono<UserCreated> handle(CreateUser cmd) { … }
bus.send(new CreateUser("alice"))
.doOnNext(this::publish)
.subscribe();// Go
cqrs.Register[CreateUser, UserCreated](bus, func(ctx context.Context, cmd CreateUser) (UserCreated, error) {
// …
})
out, err := cqrs.Send[CreateUser, UserCreated](ctx, bus, CreateUser{Name: "alice"})Validation is lifted to the message: Validate() error makes the type
implement cqrs.Validatable. Caching: CacheTTL() time.Duration
makes a query implement cqrs.Cacheable.
// Java (Spring WebFlux)
@Bean
WebFilter idempotencyFilter() { return new IdempotencyFilter(...); }// Go
mux := http.NewServeMux()
// … register routes …
core := startercore.New(startercore.Config{AppName: "orders"})
http.ListenAndServe(":8080", core.Middleware()(mux))core.Middleware() is a single func(http.Handler) http.Handler that
wraps panic-recovering ProblemDetail rendering, correlation-id
propagation, and idempotency.
// Java (R2DBC)
public interface UserRepository extends R2dbcRepository<User, String> { }// Go
type UserRepository interface {
data.Repository[User, string]
}
repo := data.NewMemoryRepository[User, string](func(u User) string { return u.ID })A pgx-backed data.Repository implementation is in scope for the next
release; today services that talk to PostgreSQL define their own
typed repository conforming to data.Repository[T, K].
// Java
@Saga
class CheckoutSaga {
@Step
void reserveStock(...) { ... }
@Compensation
void releaseStock(...) { ... }
}// Go
saga := orchestration.Saga{
Name: "checkout",
Steps: []orchestration.Step{
{Name: "reserve", Execute: reserve, Compensate: release},
{Name: "charge", Execute: charge, Compensate: refund},
},
}
out, err := saga.Run(ctx)Compensation policy maps directly: CompensateBestEffort (default) /
CompensateStopOnError.
// Java
@Idempotent("Idempotency-Key")
@PostMapping("/orders")
Mono<Order> place(@RequestBody PlaceOrder cmd) { … }In Go, idempotency is a middleware applied at the framework boundary —
applied automatically by core.Middleware() for every POST/PUT/PATCH
that carries an Idempotency-Key header.
| Java (Spring Boot YAML key) | Go field |
|---|---|
firefly.app.name |
startercore.Config.AppName |
firefly.cache.adapter |
cache.Adapter injection |
firefly.eda.broker |
eda.Broker injection |
firefly.idempotency.ttl |
web.IdempotencyConfig.TTL |
There is no application.yml parser in this release — services build
their Config struct in main.go. A YAML mapper is on the
CONFIGURATION.md roadmap.