Package Structure: Organizing by Feature, Not by Layer, and the Dependency Graph That Reveals Whether It Worked
Package Structure
The logistics platform’s package structure organizes code by technical layer:
com.logitrack
├── controller/
├── service/
├── repository/
├── model/
├── dto/
└── util/
A developer asked to understand “how shipment tracking works” must read code scattered across six packages. The controller for tracking is in controller/. The service is in service/. The repository is in repository/. The domain model is in model/. The DTOs are in dto/. The helper methods are in util/. Understanding one feature requires opening files in every package. Nothing in the structure groups related code together.
A developer asked to understand “how the controller layer works” can read one package and see every controller. This is useful if the task is “change how all controllers handle authentication.” It is useless for every other task. Feature development is the common case. Layer-wide changes are the exception. The package structure optimizes for the exception.
Feature-based packaging organizes code by business capability:
com.logitrack
├── shipment/
│ ├── tracking/
│ ├── creation/
│ └── labeling/
├── warehouse/
│ ├── inventory/
│ └── picking/
├── carrier/
│ ├── integration/
│ └── rating/
├── billing/
│ ├── invoicing/
│ └── reconciliation/
└── shared/
├── address/
└── notification/
A developer asked to understand “how shipment tracking works” opens one package. Everything related to tracking is there: the controller, the service, the repository, the domain model, the events. The developer does not need to know the rest of the codebase exists. The package boundary is the isolation boundary.
This diagram shows the logistics platform’s package structure before and after restructuring. On the left, the layer-based layout creates cross-cutting dependencies: every feature touches every package. On the right, the feature-based layout groups related code, and the dependency arrows between features are explicit and minimal. The shared package is kept small. The key metric: in the layer-based layout, understanding shipment tracking requires reading code in 6 packages. In the feature-based layout, it requires reading code in 1 package.