Organize Your Code by Data/Type, Not Functionality

A lot of projects assume that you'll put things with the same functionality in the same place, no matter what data they deal with. This makes things harder to break apart later.

Most projects keep organized by the functionality each component do. For example, all the models are in the same place, all the functions that convert one model into an internal structure/DTO are kept together, and so on. Something like this:

+-- IncomingModels
|   +-- DataTypeInterface
|   +-- DataType1
|   +-- DataType2
|   +-- DataType3
+-- Filters
|   +-- FilterInterface
|   +-- FilterValidDataType2
+-- Processors
|   +-- ProcessorInterface
|   +-- ConvertDataType1ToDto1
|   +-- ConvertDataType2ToDto2
+-- OutgoingModels
    +-- DtoInterface
    +-- Dto1
	+-- Dto2

This is fine and works. But when you organize by data, it'll make a lot easier to split your project in smaller projects -- 'cause, at some point, you may want to do almost the same thing as you're doing right now, but with small differences.

+-- Base
|   +-- IncomingModels
|   |   +-- DataTypeInterface
|   +-- Filters
|   |   +-- FilterInterface
|   +-- Processors
|   |   +-- ProcessorInterface
|   +-- OutgoingModels
|       +-- DtoInterface
+-- Data1
|   +-- IncomingModels
|   |   +-- DataType1
|   +-- Processors
|   |   +-- ConvertDataType1ToDto1
|   +-- OutgoingModels
|       +-- Dto1

Now you can make a module that deals only with Data1, another that works only with Data2 and so on. And then you can break them into isolated modules.

And then when you have another project that also have Data1 but also deals with Data3, you can reuse most of the stuff in the Data1 module.

And I do understand that this creates an explosion of directories/packages, which may seem a bit unnecessary.

Believe me, I also thought the idea of keeping things by functionality made more sense. But in one project, I got a requirement to do almost the same thing as I was doing before, but with a small change, which would require one less step/transformation (in our example, you can think as the new requirement as doing exactly what the Data1, Data2 and Data3 did, with their transformations and such, but without the Data3 part). By breaking by their types, I managed to create small modules for each one and the new project would simply reference Data1 and Data2, but not Data3.