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.
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 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
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.
A improved structure for the project, not yet completed though
Moving ValueObjects and Entities
We still have a folder for both
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
item.go in the root, with the package name tavern This leaves us with an even more improved structure.
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.
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
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 name
go 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,
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
Making this change requires the
Tavern package to reference
order.OrderService instead of the only
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.
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
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
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.
Email : email@example.com
Website : https://programmingpercy.tech