DEV Community

Amol
Amol

Posted on • Originally published at amol.me on

Code that changes together, stays together

In traditional clean/layered/onion architecture code is organized in layers and so as the abstractions such as controllers, services, repositories.

Clean Architecture

What I have seen most of people do is tried to organize code per layer by structuring code around those layer.
For example

- src
 - controllers
 - services
 - use case
 - entities
 - repositories
Enter fullscreen mode Exit fullscreen mode

In some projects, where I see team wanted to build modular monolith, they started to group based on some heuristic, that these pieces goes together probably we should modularize it.

- src
 - module#1
  - controllers
  - services
  - use case
  - entities
  - repositories
 - module#2
  - controllers
  - services
  - use case
  - entities
  - repositories
Enter fullscreen mode Exit fullscreen mode

I consider this way of modularizing code base as premature as it suffers from forcing you to make decision when you the least knowledge about the system. Sometimes this kind of modularizing felt so artificial.

So I was wondering 🤔

  • how we could defer decision of making modularizing code structure as much as I can?
  • How can I let code structure evolve itself organically?

There was another problem, If I want to make any changes, I have to make changes in upon 4-5 different packages such as controller, services, use case. In lot of cases I felt that it's lot of ceremony and dancing around so many files. There was urge of keeping things together.

Suddenly something clicked

"Neurons that fire together wire together" or "Family thats eats together, stays together"

which leads to

**Code* thats changes together, stays together*

vertical slice

vertical slice

As I move code which was changing together closer, 2 things happened

  1. Code thats was coming closer, coupling between them increased(on vertical access).
  2. Coupling between different slices started to drop

After spending more time with this approach, I learned that code thats shared across slices bubbled up and moved into core of the feature or is a domain concept. or it's just cross cutting concern. which then could moved up and finds it own place not just in code base but also conceptually.

In summary this approach provide me following flexibilities

  • ✅ Code structure can evolve as organically as with our understanding of the domain concepts.
  • ✅ Provide a way be more pragmatic per slice.
  • ✅ Provide way of grouping which can scale with growing feature sets.

Final code structure was similar to 👇🏼

- src
 - todo
  - api
   - dto
   - controller
  - find-todos
  - complete-todo
  - new-todo
  - todo
 - notes
  - api
   - dto
   - controller
  - new-notes
  - recent-notes
  - note
Enter fullscreen mode Exit fullscreen mode

Top comments (0)