In 2008, Citerus developed a Java sample application in close collaboration with Eric Evans, based on the examples in his book. The purpose was to showcase the concepts from DDD in a real-world application. Since then, the application has been ported to other languages such as C# and Ruby. Of course, as a DDD practitioner and a Go developer I thought it might be a fun exercise to try porting it to Go.
This article is intended for both DDD practitioners looking at trying out Go as well as gophers who might not know about DDD but are interested in building enterprise applications. Hopefully it will be able to serve as inspiration for how a modern enterprise application using domain driven design could look like.
This application only highlights some of the software patterns in DDD. The reader should be aware of the issues related to sample applications. If you are interested in learning DDD you really should be reading Domain Driven Design.
DDDelivery – we’re ubiquitous!
The purpose of the project is to provide flexible booking and tracking of large containers that are shipped around the world.
There are three applications involved in this project:
- goddd: The core service is responsible for tasks like booking and tracking cargos. I will explain this one in more detail.
- pathfinder: The
pathfinderservice demonstrates the use of a bounded context and returns potential routes between two locations. Services like this are typically developed by another team and will most likely have different names for some of the shared concepts.
- frontend: Finally, the frontend is mainly for understanding of how the different use cases interact with each other.
This article focuses on the core service but I encourage the reader to check out the other applications and how they interact with each other.
Porting a Java application to Go
When I started off, my priority was to get it up and running with full functionality, using minimal effort. I decided to turn it into a RESTful web service, Java packages were turned into Go packages and domain objects were turned into structs and methods. This was the easy part, because I knew that the initial version was going to be far from pretty and that it was going to be a lot work in making it idiomatic Go. I have since then worked on, among other things, reducing stuttering and names and concepts that have felt overly verbose when ported to Go. Something that has also been gradually evolving is how to actually structure the application.
Structuring the application
Go promotes using packages, not for code organization, but for actually providing cohesive units of functionality. The original application has a structure that is inspired by the Hexagonal Architecture with directories named after each layer such as
interfaces. I wanted a flat directory structure with root-level domain packages with names that communicate what they provide as opposed to what they contain, which meant no
At the time of writing, I have the following packages:
That is a lot of packages so let me briefly explain them.
tracking contains the application services corresponding to the three use-cases.
booking is used by one of our people to book and manage cargos.
handling is used by the staff at each location in order to report that a cargo has been received and so on. Finally,
tracking allows a customer to view details about their cargo.
voyage are pure domain packages that are used by the application services.
repository contains in-memory implementations of the repositories defined in the domain packages.
middleware is where a lot of the stuff required by an enterprise application reside, like logging, metrics, circuit breaking and so on.
Finally, the service provided by
routing is meant to serve as a proxy for the pathfinder service.
What I like about this structure is that it is flat. There are no packages in hiding. Since the packages have names that are present in the domain language (apart from maybe
middleware), it becomes easy to scan the root directory and get a feeling about the scope of the application.
What I do not like is that some of the packages, such as
voyage are relatively small (due to my initial thought of putting domain aggregates in their own packages). One option would might have been to collapse them to one single
domain package, but that would have been too generic. That would mean I probably would collapse application services into one
services package, and then we would be back at where we started.
This is obviously a work in progress and very much experimental in its nature. There is a lot of things to improve but I would like this article to invite both the Go community as well as DDD practitioners, to contribute by sharing your thoughts and filing issues. Pull requests are more than welcome!
In the second part of this article, we will have a look at some code. We will examine how the building blocks of DDD have been implemented as well as how we deal with common challenges in distributed systems. Stay tuned!