Organize Code By Feature

Organize code by feature to enable spin out smaller services later

Luke ShannonLuke Shannon

Package Structure As A Gateway To Microservices

This blog shares an insight I recently got from my good friend Josh Long. To many of you it might be obvious, but where I'm at with my Microservices journey, it was a great realization that I wanted to share.

Does this look like your source code?

For years I have had package names like this:

  • Services
  • Controllers
  • Model
  • Repo

The feedback I got from my good friend Josh was to create packages based on Features and not components.

Now I have this.

Each of these packages is named after a feature in my app.

What's so great about this, you ask?

Granularity Can Be Tricky

Like many, I have been reading and researching Microservices.

My first journey into Microservices resulted in an application that had a group of services and some Netflix OSS bells and whistles. I felt really smart and cool.

But it was annoying to deploy and not fun to troubleshoot.

I ended up writing lots of shell scripts and using Monit to keep the system "alive."

A platform like Cloud Foundry (Hosted version) can handle infrastructure management. PCF creates nice workflows for developers, which helps with some of the pain of operating multiple distributed services. But I still went too far from an architectural point of view; at least one of these services could have existed within another. What's the alternative?

Is Starting With Microservices Smart?

If you look at the some of the better examples of this Microservice-style architecture, Netflix and Amazon both moved towards this structure out of necessity.

For example, Netflix wanted to move from shipping DVDs towards streaming video to users around the globe. They could not do that with a large and complex monolith. This blog has a great explanation of why that was.

Amazon went from selling books online to selling everything (remember staring at Amazon Prime for half the day looking for deals?), and they included the same on-demand infrastructure to make their architectural shift possible (AWS).

These successful internet giants did not start with a "Microservice" structure; they moved this way out of necessity. They were successful with monolithic architectures first, but microservice-style architectures allowed them to scale and evolve.

This idea of starting with a monolith and moving towards Microservices has been thrown out there before: https://martinfowler.com/bliki/MonolithFirst.html

Sam Newman's book references "Seams" in the code as the boundaries for services and includes a guide on how to peel out code: http://shop.oreilly.com/product/0636920033158.do

Interesting ideas. But....

Breaking Up Monoliths Is Hard!

If you are developing in an enterprise, chances are that you have seen big, tightly coupled code base that won't leverage the scalable nature of cloud computing. Nobody starts with bad intentions when developing an application, but over years, with multiple teams, contractors, tight deadlines, fixes and patches...well...it becomes a tangled mess.

Breaking out functionality in a legacy application is possible, but it's a bit like unplugging and tracing wires in the picture above.

Moving Forward Organized By Feature

So, starting with a monolith does not mean you have created a tangled mess.

By organizing all the components (Model, Controllers, Services, Repos) by Feature, instead of a tangled mess you have more order in the way the code is grouped.

Then, the task of peeling out functionality is less daunting than before.

We can break out a package or feature into to a separate service when:

  • A package gets too full of components and requires multiple team members to maintain
  • The functionality of a package needs to be exposed outside of the context of the application
  • We want to re-write a feature in a language or framework better suited for the problem the feature or package is addressing

In theory, I hope to simply copy the package or feature into a new Spring Boot app, add the correct dependancies, update the tests and then have a new service to deploy. I'm sure there will be more to it than that, but at least there is a clear starting place now.

Time will tell how well this works, but going forward I plan to be less aggressive in peeling out services, but more mindful of package structures.

Happy coding, my friends.