The DDD Hamburger for Go
Tags: ddd, go, architecture, microservices
Published: 2024-01-06 19:05:46

---
title: The DDD Hamburger for Go
published: true
description: The DDD Hamburger is my favorite type of architecture for Go. Why? The DDD Hamburger beautifully combines the best of the hexagonal architecture and layered architecture. Get to know the DDD Hamburger Architecture for Go and maybe it'll become your favorite too!
tags: ddd, go, architecture, microservices
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2lvxr97astfsjxuaquuc.png
canonical_link: https://medium.com/@remast/the-ddd-hamburger-for-go-61dba99c4aaf
# Use a ratio of 100:42 for best results.
# published_at: 2024-01-06 20:00 +0000
---

The DDD Hamburger is my favorite type of architecture for Go. Why? The DDD Hamburger beautifully combines the best of the [hexagonal architecture](https://alistair.cockburn.us/hexagonal-architecture/) of [Domain-Driven-Design](https://martinfowler.com/bliki/DomainDrivenDesign.html) and layered architecture. Get to know the DDD Hamburger Architecture for Go and maybe it'll become your favorite too!

## The DDD Hamburger Overview 🍔

![DDD Hamburger Overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0kz65ijk1wf1029sj1b.png)

The DDD Hamburger is a real Hamburger with well defined layers from top to bottom:

**The upper Bun half** 🍞
The upper bun half is the top of the Hamburger and is the _presentation layer_. The presentation layer contains your REST API and the web interface. 

**The Salad** 🥗
Below the bun is the salad which is the _application layer_. The application layer contains the use case logic of your application. 

**The Meat** 🥩
Next is the meat of your Hamburger, the _domain layer_. Just like the meat the domain layer is the most important part of your Hamburger. The domain layer contains you domain logic and all entities, aggregates and value objects of your domain.

**The lower Bun half** 🍞
The lower bun half is the _infrastructure layer_. The infrastructure layer contains concrete implementations of your repositories for a Postgres database. The infrastructure layer implements interfaces declared in the domain layer.

Got the idea? Great, so let's look into the details.

## Go Example of the DDD Hamburger

Now we'll code a Go application using our DDD Hamburger. We'll use a simple time tracking example application with activities to show the practical Go implementation. New activities are added using a REST API and are then stored in a Postgres database. 

The DDD Hamburger Architecture applied to Go is shown below. We'll go through all layers of the Hamburger in detail soon.

![Go Example of the DDD Hamburger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bjg2demyj0rstuiren6.png)

## The Presentation Layer as upper Bun 🍞

The presentation layer contains the HTTP handlers for the REST API. The HTTP handlers like `HandleCreateActivity` create simple handler functions. All handlers for activities hang off a single struct `ActivityRestHandlers`, as the code below shows. 

```go
type ActivityRestHandlers struct {
	actitivityService  *ActitivityService
}

func (a ActivityRestHandlers) HandleCreateActivity() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
        // ...
	}
}
```
The actual logic to create a new activity is handled by a service 
`ActivityService` of the underlying application layer.

The HTTP handlers don't use the activity entity of the domain layer as JSON representation. They use their own model for JSON which contains the struct tags to serialize it to JSON properly, as you see below.

```go
type activityModel struct {
	ID          string        `json:"id"`
	Start       string        `json:"start"`
	End         string        `json:"end"`
	Duration    durationModel `json:"duration"`
}
```

That way our presentation layer only depends on the application layer and the domain layer, not more. We allow relaxed layers, which means it's ok to skip the application layer and use stuff from the domain layer directly.

## The Application Layer as Salad 🥗

The application layer contains service which implement the use cases of our application. One use case is to create an activity. This use case is implemented in the method `CreateActivity` that hangs of the application service struct `ActitivityService`. 

```go
type ActitivityService struct {
	repository ActivityRepository
}

func (a ActitivityService) CreateActivity(ctx context.Context, activity *Activity) (*Activity, error) {
	savedActivity, err := a.repository.InsertActivity(ctx, activity)
    // ... do more
	return savedActivity, nil
}
```

The application services uses the repository interface `ActivityRepository` for the use case. Yet it only knows the interface of the repository which is declared in the domain layer. The actual implementation of that interface is not of concern to the application layer.

The application services also handle the transaction boundaries, since one use case shall be handled in one single atomic transaction. E.g. the use case of creating an new project with an initial activity for it has to go through in one transaction although it'll use one repository for activities and one for projects.

## The Domain Layer as Meat 🥩

The most important part is the domain layer which is the meat of our DDD hamburger. The domain layer contains the domain entity `Activity`, the logic of the domain like calculating the activity's duration and the interface of the activity repository.

```go
// Activity Entity
type Activity struct {
	ID             uuid.UUID
	Start          time.Time
	End            time.Time
	Description    string
	ProjectID      uuid.UUID
}

// -- Domain Logic
// DurationDecimal is the activity duration as decimal (e.g. 0.75)
func (a *Activity) DurationDecimal() float64 {
	return a.duration().Minutes() / 60.0
}

// ActivityRepository
type ActivityRepository interface {
	InsertActivity(ctx context.Context, activity *Activity) (*Activity, error)
    // ... lot's more
}
```

The domain layer is the only layer that's not allowed to depend on other layers. It should also be implemented using mostly the Go standard library. That's why we neither use struct tags for json nor any database access code.

## The Infrastructure Layer as lower Bun 🍞

The infrastructure layer contains the concrete implementation of the repository domain interface `ActivityRepository` in the struct `DbActivityRepository`. This repository implementation uses the Postgres driver [pgx](https://github.com/jackc/pgx) and plain SQL to store the activity in the database. It uses the database transaction from the context, since the transaction was initiated by the application service.

```go
// DbActivityRepository is a repository for a SQL database
type DbActivityRepository struct {
	connPool *pgxpool.Pool
}

func (r *DbActivityRepository) InsertActivity(ctx context.Context, activity *Activity) (*Activity, error) {
	tx := ctx.Value(shared.ContextKeyTx).(pgx.Tx)
	_, err := tx.Exec(
		ctx,
		`INSERT INTO activities 
		   (activity_id, start_time, end_time, description, project_id) 
		 VALUES 
		   ($1, $2, $3, $4, $5)`,
		activity.ID,
		activity.Start,
		activity.End,
		activity.Description,
		activity.ProjectID,
	)
	if err != nil {
		return nil, err
	}
	return activity, nil
}
```

The infrastructure layer depends on the domain layer and may use all entities, aggregates and repository interfaces from the domain layer. But from the domain layer only.

## Assemble the Burger in the Main Function

No we have meat, salad and bun lying before us as single pieces. It's time to make a proper Hamburger out of these pieces. We assemble our Hamburger in the main function, as you see below.

To wire the dependencies correctly together we work from bottom to top:
1. First we create a new instance of of the db activity repository `DbActivityRepository` and pass in the database connection pool. 
2. Next we create the application service `ActivityService` and pass in the repository.
3. Now we create the `ActivityRestHandlers` and pass in the application services. We now register the HTTP handler functions with the HTTP router.

The code to assemble our DDD Hamburger architecture is like this:

```go
func main() {
    // ...

    // Infrastructure Layer with concrete repository
	repository := tracking.NewDbActivityRepository(connectionPool)

    // Application Layer with service
    appService := tracking.NewActivityService(repository)
	
    // Presentation Layer with handlers
    restHandlers := tracking.NewActivityRestHandlers(appService)
    router.HandleFunc("/api/activity", restHandlers.HandleCreateActivity())
}
```

The code to assemble our Hamburger is plain, simple and very easy to understand. I like that, and it's all I usually need.

## Package Structure for the DDD Hamburger

One question remains: Which structure of our Go packages is best suited for the DDD Hamburger? 

I usually start out with a single package for all layers. So a single package `tracking` with files `activity_rest.go` for the rest handlers, `activity_service.go` for application services, the domain layer in `activity_domain.go` and the database repository in `activity_repository_db.go`.

A next step is to separate out all layers as separate packages, except the domain layer. So we'd have a root package `tracking`. The root package contains the domain layer. The root package has sub package for each layer like `application`, `infrastructure`, `presentation`. Why the domain layer in the root package? That way we can use the domain layer with it's proper name. So if we'd use the domain entity `Activity` somewhere, the code would read `tracking.Activity` which is very nice to read.

Whichever package structure is best depends on your project. I'd suggest you start small and easy and adjust it as your project grows over time.

## Wrap Up of the DDD Hamburger 🍔

The DDD Hamburger is a layered architecture based fully on Domain Driven Design. It's very easy to understand and follow. That's why the DDD Hamburger is my favorite architectural style. In general and especially in Go.

Using the DDD Hamburger for your Go application is pretty straightforward, as you've seen. You can start out small yet you're able to grow as needed.

See an application of the DDD Hamburger below:
{% github https://github.com/Baralga/baralga-app %}

## Credits

The DDD Hamburger Architecture was coined by [Henning Schwentner](https://www.wps.de/wps/team/henning-schwentner), so thanks for that. Another great influence for structuring the HTTP handlers came from [Mat Ryer](https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html).