Coding: Packaging by vertical slice
On most of the applications I’ve worked on we’ve tended to organise/package classes by the function that they have or the layer that they fit in.
A typical package structure might therefore end up looking like this:
-
com.awesome.project
-
common
-
StringUtils
-
-
controllers
-
LocationController
-
PricingController
-
-
domain
-
Address
-
Cost
-
CostFactory
-
Location
-
Price
-
-
repositories
-
LocationRepository
-
PriceRepository
-
-
services
-
LocationService
-
-
This works reasonably well and allows you to find code which is similar in function but I find that more often than not a lot of the code that lives immediately around where you currently are isn’t actually relevant at the time.
On the last couple of applications that I’ve worked on we’ve been trying to group code around a domain concept or vertical slice of functionality.
Therefore instead of the above code we’d end up with something more like this:
-
com.awesome.project
-
location
-
Address
-
Location
-
LocationController
-
LocationRepository
-
LocationService
-
-
platform
-
StringUtils
-
-
price
-
Cost
-
CostFactory
-
Distance
-
Price
-
PriceController
-
PriceRepository
-
-
We were having a discussion about grouping code like this last week and I was struggling to describe what I prefer about the latter approach.
In the code base that I’m currently working on, which provides an API for other systems to do stuff with, it seems to lead to a design where we have created lots of potential micro services which could be deployed separately if we wanted.
That possibility wasn’t as clear to me until we started grouping code this way.
Another cool thing is that it’s made us think about the domain of the code more and whether the grouping of classes actually makes sense. We can also see which classes fall inside an aggregate root.
In the above example under 'pricing' we can tell that Price is an aggregate root because it has a repository which allows us to get one and we can also tell that Cost is probably contained by Price since we don’t have a way of directly getting a Cost.
We stop thinking about the domain classes as a whole, instead we think about them in their groups and how their aggregate roots might interact with each other if at all.
One disadvantage of grouping code like this is that if we’re writing a new repository, for example, we’ve got further to navigate to find another one to base ours on.
On the other hand you could argue that if we’re doing that then perhaps there’s an abstraction we can pull out to remove the problem.
It’s an interesting approach to grouping code and one thing we’ve started noticing is that we end up with some packages which have a lot of classes in them and others which have very few.
We’re not sure whether this is a symptom of us not breaking down those particular packages enough or if there are just some areas of the domain that are bigger than others.
These are just some of my early observations so it’d be interesting to hear other’s thoughts on whether this is a good/bad idea.
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.