Return to site

🏗️🧩 PACKAGE BY FEATURES: WHEN “LAYERS” AREN’T ENOUGH

· programmer,techlead,architecture,cleancode

🔸 TL;DR

Layered packages are fine — sometimes they’re exactly what you need.

But when complexity grows, package-by-feature keeps changes local and reduces accidental coupling. Add package-private internals to enforce boundaries. If you want automated checks + docs + module testing, Spring Modulith builds on the same idea with tooling.

Section image

🔸 THREE-TIER ARCHITECTURE (THE CLASSIC)

The traditional split is:

▪️ View → controllers / web layer (HTTP, UI)

▪️ Business → services / domain logic

▪️ Data → repositories / persistence

This separation still makes sense — but the way we mirror it in packages can become limiting as the app grows.

🔸 THE CLASSIC “PACKAGE BY LAYERS” CODEBASE

Typical structure:

▪️ controller/ → all controllers

▪️ service/ → all services

▪️ repository/ (or data/) → all repos

▪️ model/ → all entities/DTOs

✅ Works well when:

▪️ the codebase is small/medium

▪️ the domain is simple

▪️ the team is stable and conventions are strong

⚠️ Gets harder when features multiply:

😵 understanding “Orders” requires jumping across many packages

😵 changes spread across the whole project

😵 “service calls service calls service” becomes common

🔸 PACKAGE BY FEATURES (VERTICAL SLICES)

Instead of grouping by type, group by feature:

▪️ features/orders/

▪️ controller + service + repo + model

▪️ features/payments/

▪️ controller + service + repo + model

✅ Benefit: when you work on a feature, you stay in one place.

✅ Code that changes together… lives together. 📦

🔸 ENFORCE BOUNDARIES WITH PACKAGE-PRIVATE

package-private:

▪️ Make internals package-private: impls, helpers, repos, most models

▪️ Expose only a small public API per feature (facade / interface)

▪️ Avoid cross-feature “quick calls” that slowly create coupling chaos

🧠 Goal: features collaborate intentionally — not accidentally.

🔸 WHAT ABOUT SPRING MODULITH?

▪️ Package-by-feature =a structuring strategy + conventions you enforce

▪️ Spring Modulith =the same mindset plus tooling to model and validate modules

Spring Modulith can help you:

▪️ define/detect modules

▪️ verify boundaries (no illegal dependencies, no cycles)

▪️ generate documentation/diagrams

▪️ test modules with dedicated support

…as part of a “modular monolith” approach in the Spring ecosystem

🔸 TAKEAWAYS

▪️ Packaging by layers isn’t “wrong” — it just has scaling limits

▪️ Package-by-feature improves navigation and keeps changes localized

▪️ Package-private helps prevent cross-feature spaghetti

▪️ Spring Modulith adds verification, documentation, and module testing to this modular approach

#java #spring #springboot #architecture

Go further with Java certification:

Java👇

Spring👇

SpringBook👇

JavaBook👇