How To Structure DDD in Go

Posted by : at

Category : go   guides

It is time to take a look at a better architectural solution when using DDD in Go projects. In this article, we will take a repository that has all the DDD components in place and show how a less complicated project setup can be managed, while still maintaining DDD.

The repository that we will change is from a previous article I wrote on how to implement DDD in Go. In that article, I explained all the components present in DDD as explained by Eric Evans. The repository can be found on GitHub.

The first article can be found here How To Implement DDD in Golang

All images drawn in this article are drawn by Percy Bolmér, the Gopher is drawn by Takuya Ueda, inspired by the works of Renée French (CC By 3.0). The gopher has been modified in the images

Moving Aggregates to Their Domain Package

The first thing to do is to remove the aggregate package altogether. We learned what an Aggregate is and what rules to apply, we don’t necessarily have to name the package to aggregate. I tend to place the Aggregates in their respective domain package, so the Customer aggregate should go inside the customer domain. It makes a lot more sense in my opinion.

Moving the Customer aggregate

Removing the Customer aggregate from the Aggregate package

Of course, after moving the files to the customer domain, you also need to change any reference that is pointing to aggregate.Customer and replace it with customer.Customer or Customer if you’re in the customer package.

customer/repository.go — Removing all mentions of aggregate package The same thing has to be performed for the Product aggregate, it should go inside the product domain. I won’t cover all the code changes, it should be easy enough to find and refactor all mentions of the aggregate package.

Moving the prodcut aggregate

The image shows the Product domain with the product aggregate as part of it

According to me, it looks a lot nicer having the aggregate in their domain package in this way, it also kind of makes more sense.

By Deleting the aggregate folder and we have tidied up some of the code smell.

Improved structure without aggregates

A improved structure for the project, not yet completed though

Moving ValueObjects and Entities

We still have a folder for both entity and valueobject, which I guess isn’t wrong. One thing that is nice about storing the shared structs in their separate package that way is to avoid Cyclical Imports.

We can achieve this in another way though, which is less bloated. Right now we have no root package at all. Move all files from entity and valueobject to the root and rename the package to tavern

item.go in the root, with the package name tavern This leaves us with an even more improved structure.

Moved entities into root domain

Moved entities and other shared items into the root package

You will also need to rename your module in go.mod into the appropriate name.

module github.com/percybolmer/tavern

Not only that, we need to change all the imports in all the files to reflect the change, and all references of entities package have to be changed to tavern like the following gist.

Product domain Replacing all instances of entities with tavern, and all imports. All the imports starting with github.com/percybolmer/go-ddd are changed to github.com/percybolmer/tavern.

The easiest way to apply this change is to change all the files, remove the go.mod and go.sum file, and reinitialize the go module with the new namego mod init github.com/percybolmer/tavern .

That was quite the refactoring exerciser, but it was worth it. Feel free to rerun any tests so you know that it is working as expected.

Splitting the Services package

Right now, the services package contains all services in the same package. I’d like to split these into two packages, Order and Tavern.

The reason is that as projects grow it is nice to have the services divided into smaller packages. It can in my opinion become bloated storing all of them in the same package. I also like keeping the domain as part of the infrastructure, in this case, we create a new folder named order in the services folder. The reason is that in the future we might see more order-related services appearing, for instance, the current Order service focuses on Customer that orders a beverage, but what about when the Tavern needs to resupply? Using this structure it is easy for the developers to know where to look for related code.

Another important thing is the naming of the Configuration functions, if we keep creating functions like WithMemoryCustomerRepository it is hard to maintain what Configuration goes where. It is a lot easier if we see order.WithMemoryCustomerRepository to know what is happening.

Services — How the services package is structured into sub packages

Restructure services into sub services

Making this change requires the Tavern package to reference order.OrderService instead of the only OrderService

Change all references to match the new structure. We will also add a new function to the OrderService to add new customers because right now we can only do that by using the customer repository inside the struct. This won’t work since the service will be exposed to other packages. Also, a Service should never assume that the user of the service knows how to operate like this, so it is only natural that this logic is maintained in the service’s own domain.

AddCustomer in the OrderService to help creating new Customers. After applying these changes, the test to order a product is even easier to follow.

tavern_test.go — Creating a new customer and ordering is even easier Now we finally have the final solution. A very simple to navigate, clean structured project.

Final architecture of domain-driven design

The final architecture to use

How to Run The Tavern

One last thing I like to recommend is the use of the cmd folder as found in many Go repositories. In that folder, all command-line tools should be found, and it can be more than one. For the sake of concluding this article, I will create a main program in the cmd folder.

mkdir cmd  
touch main.go

Let’s order a beer to celebrate.

main.go — Try running the program to see that everything is in order, get the pun? This concludes this very long two-part series about DDD.

You can find the full code on GitHub.

There is still a lot more about DDD to learn, this only touches the basics and how I find structuring projects. We haven’t touched topics as Domain events, CQRS and EventSourcing yet. Stay tuned for more.

If you’re eager to learn more about DDD I do suggest Eric Evans book Domain-Driven Design: Tackling Complexity in the Heart of Software. You can find it on Amazon.

Please note that the link is an affiliate link which means that I do also get paid when it is used to purchase the product.

As always, feel free to reach out to me on any of my social media. I do love feedback, criticism, and even flaming.

Software Engineering is a Life Style, Not an Job »

About Programming Percy
Programming Percy

Hey, I'm Percy Bolmér. I'm a software engineer, writer and tech enthusiast. I've been professionally building software for 7+ years, and absoluetly love sharing any ideas I come across.

Email : programmingpercy@gmail.com

Website : https://programmingpercy.tech

About Percy Bolmér

Hey, I'm Percy Bolmér. I'm a software engineer, writer and tech enthusiast. I've been professionally building software for 7+ years, and absoluetly love sharing any ideas I come across.

Useful Links