◎ Go for
Java Developers
Section 19

Project Layout

comms engine — standard Go project layout
engine/
├── cmd/
│   └── engine/
│       └── main.go            // entry point — wires everything
├── internal/                  // private — CANNOT be imported outside this module
│   ├── domain/                // pure data types: Message, Event, Route
│   │   ├── message.go
│   │   └── message_easyjson.go  // generated by easyjson
│   ├── store/                 // database layer
│   │   ├── queries.sql.go     // generated by sqlc — do not edit
│   │   ├── pg_store.go        // wraps sqlc queries, implements interface
│   │   └── interfaces.go      // store interfaces defined here
│   ├── broker/                // message broker abstraction
│   │   ├── redis_broker.go    // production
│   │   └── mem_broker.go      // in-memory for tests
│   ├── engine/                // orchestration core
│   │   ├── engine.go          // Engine struct, Start/Stop
│   │   ├── router.go          // topic routing logic
│   │   └── worker.go          // worker goroutine pool
│   └── handler/               // HTTP handlers
├── pkg/                       // public — importable by external modules
│   └── middleware/
├── go.mod                     // module definition (like pom.xml)
└── go.sum                     // dependency checksums (like lockfile)

go.mod — module system

☕ Java — pom.xml / build.gradle
<!-- pom.xml -->
<groupId>com.mycompany</groupId>
<artifactId>engine</artifactId>
<version>1.0.0</version>

<dependencies>
  <dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.0.0-jre</version>
  </dependency>
</dependencies>
◎ Go — go.mod
module github.com/mycompany/engine

go 1.22

require (
    github.com/gin-gonic/gin   v1.9.1
    github.com/lib/pq          v1.10.9
    github.com/mailru/easyjson v0.7.7
    github.com/sqlc-dev/sqlc   v1.25.0
)

Java tool ↔ Go tool mapping

Jackson ObjectMapper / Gson
encoding/json by default; easyjson for hot-path JSON
Hibernate / JPA (ORM)
GORM (closest ORM analog)
SQL-first style (jOOQ mindset)
sqlc (write SQL, generate typed Go methods)
When easyjson is worth it
Start with encoding/json. Move to easyjson only when profiling proves JSON marshalling is a bottleneck and the model shape is stable enough to justify generated code maintenance.

easyjson — generated JSON marshalling

easyjson — 3-5x faster than encoding/json
// encoding/json uses reflection at runtime — allocates, slow in hot paths
// easyjson generates code at build time — no reflection, no allocations

// Step 1: annotate the file
//go:generate easyjson -all message.go

type Message struct {
    ID      int64  `json:"id"`
    Topic   string `json:"topic"`
    Payload []byte `json:"payload"`
}

// Step 2: run code generation
// go generate ./...

// Step 3: easyjson creates message_easyjson.go with:
// func (m *Message) MarshalJSON() ([]byte, error) { ... }
// func (m *Message) UnmarshalJSON(data []byte) error { ... }

// Usage — same API, generated code runs automatically:
data, err := json.Marshal(&msg)     // uses generated MarshalJSON
err = json.Unmarshal(data, &msg)    // uses generated UnmarshalJSON
When sqlc is better than ORM
Choose sqlc when queries are complex, performance is critical, or you want exact SQL control. sqlc is SQL-first: it generates Go code from SQL files and schema, not from struct tags.

sqlc — type-safe SQL without ORM

☕ Java — JPA / Hibernate ORM
@Entity
@Table(name = "messages")
class Message {
    @Id @GeneratedValue
    private Long id;

    @Column
    private String topic;
}

// JPA generates SQL — you learn JPA-QL
// N+1 problems lurk
repo.findByTopic("orders");
◎ Go — sqlc (write SQL, get Go)
// Write raw SQL in queries.sql:
// -- name: GetByTopic :many
// SELECT id, topic, payload
// FROM messages
// WHERE topic = $1
// ORDER BY created_at DESC;

// Run: sqlc generate
// Creates queries.sql.go with:
func (q *Queries) GetByTopic(
    ctx context.Context,
    topic string,
) ([]Message, error) {
    // generated — typed, no reflection
}

// Usage — fully type-safe
msgs, err := queries.GetByTopic(ctx, "orders")