DEV Community

Cover image for Golang Master Class: Struct Embedding.
Sk
Sk

Posted on

Golang Master Class: Struct Embedding.

Imagine you have three LEGO pieces: a Gatling gun, a helicopter, and a pilot.

Now smash them together, and boom! You’ve got a fighter chopper.

That’s basically struct embedding in Go: merging different components into one to create a super component.

And honestly? This is one of my favorite patterns in Go.

Here’s a real example from my current project, where I fuse three basic agents to build a super agent:

  1. A Base Agent that can respond

base agent

  1. A Function Calling Agent that embeds the Base Agent

fn call

  1. A Long Conversational Agent that embeds the Function Calling Agent

Long Conversational Agent

So the Long Convo Agent ends up inheriting behaviors from both, the Function Calling Agent and the Base Agent. It’s like nested power-ups.

Now, I know this looks like inheritance. but it’s not. Not in the OOP sense. There’s no strict relationship here. Just components merged together like modular building blocks.

Let’s bring this concept back to our Storage project, we've been working on and see how we can embed a Logger into it.

Full Code(including prev article):

git clone https://github.com/sklyt/goway.git
Enter fullscreen mode Exit fullscreen mode

Loggers: Our Next LEGO Piece

Loggers are lightweight and super easy to embed, perfect for demonstrating this pattern.

Let’s create a new folder:

logger/
  log.go
Enter fullscreen mode Exit fullscreen mode

logger.go:

package logger

import "fmt"

type Logger struct {
    Prefix string
}

func NewLogger(prefix string) *Logger {
    return &Logger{Prefix: prefix}
}

func (l *Logger) Log(msg string) {
    fmt.Printf("[%s] %s\n", l.Prefix, msg)
}
Enter fullscreen mode Exit fullscreen mode

Before we embed this into our storage structs, a quick word on constructors.


Constructors in Go

A constructor is just a function that wraps struct creation and returns the struct. You’ve already seen one above:

func NewLogger(prefix string) *Logger {
    return &Logger{Prefix: prefix}
}
Enter fullscreen mode Exit fullscreen mode

This makes creating components clean and consistent.

Now let’s apply this to our storage system.


Embedding the Logger in OnlineStore

type OnlineStore struct {
    *logger.Logger
    *gohttplib.Client
    URL string
}

func NewOnlineStore(url string) *OnlineStore {
    return &OnlineStore{
        Logger: logger.NewLogger("ONLINE"),
        Client: gohttplib.NewClient(url, httpOpts...),
        URL:    url,
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice this line:

*logger.Logger
Enter fullscreen mode Exit fullscreen mode

We’re embedding a pointer to our logger directly into the OnlineStore. And during creation, we pass in a prefixed logger instance.


Embedding the Logger in LocalStore

type LocalStore struct {
    *logger.Logger
    Path  string
    store map[string][]byte
}

func NewLocalStore(path string) *LocalStore {
    return &LocalStore{
        Logger: logger.NewLogger("LOCAL"),
        Path:   path,
        store:  make(map[string][]byte),
    }
}
Enter fullscreen mode Exit fullscreen mode

Now both our storage types are enhanced with logging behavior, plug-and-play style.


Updating main.go

Let’s switch from manual struct creation to using constructors:

func main() {
    online := storage.NewOnlineStore("https://jsonplaceholder.typicode.com")
    local := storage.NewLocalStore("./data")

    // Use them like before, now with logging!
}
Enter fullscreen mode Exit fullscreen mode

And that’s struct embedding in a nutshell: building composable, extendable components without the chains of inheritance.

We just went from plain storage to log-enhanced modular storage, with zero friction.


In the next article, we’ll explore:

  • Functional Options

Top comments (7)

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

Pretty cool seeing Go handled like this, really makes me wanna mess around with new combos myself

Collapse
 
sfundomhlungu profile image
Sk

Facts, honestly I didn't know any of this until a few weeks back! changed the way I viewed Go. it's so fun now! 🔥 it's literally like building LEGO!

Collapse
 
ansilgraves profile image
Ansil Graves

Great breakdown of struct embedding! I love how composable Go structs can be. Has anyone else used struct embedding in their projects? Any cool patterns or tips to share?

Collapse
 
sfundomhlungu profile image
Sk

Thanks!, oh yeah I use them all the time! like Agentic project and large Go OSS code bases! they are good to know before reading any codebase.

Collapse
 
dotallio profile image
Dotallio

Love the LEGO analogy, makes it so clear. How do you keep things modular with embedding as projects grow bigger?

Collapse
 
sfundomhlungu profile image
Sk • Edited

Great question; I used to struggle with this a lot too!
But the trick is to use your folders as separators or logical groups, especially in Go. Let me show you what I mean:

Golang Folder Structure

In the image, if you look at the pkg/agent folder, each file contains a single base struct.
No other major structs are allowed; unless it's a small helper or supporting type.

So the idea is this:
You have a folder (e.g., agent) under one Go package agent, and inside that folder, each file holds one “main” struct.
From there, you can embed easily.

For example:

  • agent.go contains the base agent
  • fn_call.go embeds that base agent
  • dialogueagent.goembeds fn_call.go

It’s basically dependency injection by structure; but done with folders and files.

TL;DR:

  • Group related structs under the same package + folder
  • Each file = one major struct
  • Embedding becomes dead simple
  • If your system gets really big, then bring in proper dependency injection and collapse the logic into a service layer

Some comments may only be visible to logged-in visitors. Sign in to view all comments.