
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.
How To Implement Domain-Driven Design (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. 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.

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.
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.

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.
Deleting the aggregate folder and we have tidied up some of the code smell.

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
This leaves us with an even more improved structure.

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.
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.

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.
After applying these changes, the test to order a product is even easier to follow.
Now we finally have the final solution. A very simple to navigate, clean structured project.

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.
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.
As always, feel free to reach out to me on any of my social media. I do love feedback, criticism, and even flaming.
I’m available here on medium, Twitter, Instagram, and Linkedin.