Why Clean Architecture Confuses Everyone (And How I Learned to Stop Worrying)

Published: (February 4, 2026 at 09:03 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

The Framework Problem

When you start a Spring Boot project, the framework practically begs you to do this:

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String customerEmail;

    @OneToMany(mappedBy = "order")
    private List items;
}

@Service
public class OrderService {
    @Autowired
    private OrderRepository repository;

    public Order save(Order order) {
        return repository.save(order);
    }
}

It works. It’s fast to write. Your DevOps team is happy because you delivered quickly. But here’s the problem: your business logic now knows about JPA, Jackson, and probably a dozen other framework concerns.

Clean architecture says your domain shouldn’t know about these things. Yet Spring Boot is right there, making it so easy to just add one more annotation.

The Package‑by‑Layer Trap

Most tutorials teach you this structure:

src/main/java/
├── controller/
├── service/
├── repository/
└── model/

It feels natural. It’s what everyone does. But it completely misses the point of clean architecture.

The problem isn’t the folders—it’s that this structure encourages you to think in terms of technical layers instead of business capabilities.

When you organize by feature instead:

src/main/java/
├── order/
│   ├── domain/
│   ├── application/
│   └── infrastructure/
├── product/
└── customer/

Something interesting happens: you start thinking about what the system does rather than what technologies it uses.

What I Learned Building Real Systems

In previous roles, I’ve worked on high‑throughput billing systems, real‑time data platforms, and multi‑tenant public‑administration systems. Here’s what actually mattered:

1. Business Logic Should Be Boring

Your core business rules shouldn’t care about:

  • How data is stored (PostgreSQL? MongoDB? Firestore?)
  • How requests arrive (REST? GraphQL? Message queue?)
  • Which framework you’re using

When I needed to migrate a system from MongoDB to PostgreSQL, the pain level told me everything about the architecture quality. If your business logic is tangled with database annotations, that migration becomes a nightmare.

2. Interfaces Without Purpose Are Noise

Don’t create interfaces just because someone said “clean architecture needs ports and adapters.” Create them when you actually need to swap implementations or isolate dependencies.

I’ve seen codebases with repository interfaces that have exactly one implementation, forever. That’s not architecture—that’s ceremony.

3. The Real Test Is Change

Can you:

  • Switch from PostgreSQL to another database without touching business logic?
  • Change your REST API to use GraphQL without rewriting services?
  • Replace Spring Boot with Micronaut (hypothetically)?

If the answer is “yes,” you probably have decent architecture. If the answer is “are you insane?”, then the folder structure doesn’t matter—you’re coupled.

The Practical Middle Ground

Keep business rules clean

public class CancelOrderUseCase {
    private final OrderRepository orderRepository;
    private final PaymentGateway paymentGateway;

    public void execute(Long orderId, String reason) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFound(orderId));

        if (order.isAlreadyShipped()) {
            throw new CannotCancelShippedOrder(orderId);
        }

        if (order.isPaid()) {
            paymentGateway.refund(order.getPaymentId());
        }

        order.cancel(reason);
        orderRepository.save(order);
    }
}

Notice: no @Service, no @Transactional, no JPA annotations. Just business logic.

Adapt at the boundaries

@Service
@Transactional
public class OrderService {
    private final CancelOrderUseCase useCase;

    public void cancelOrder(Long orderId, String reason) {
        useCase.execute(orderId, reason);
    }
}

Now the Spring stuff lives at the edge. The business logic doesn’t know about it.

Stop Overthinking, Start Measuring

Instead of debating folder structures, ask yourself:

  • How hard is it to test my business logic?
    If you need to spin up a database or mock 15 dependencies, something’s wrong.

  • How often do framework updates break my code?
    When Spring Boot 4.0 dropped, how many files did you have to touch?

  • Can a new developer understand what the system does?
    If they have to read through controller → service → repository → entity just to understand one feature, your architecture is hiding the business logic.

What Actually Matters

After building systems across different industries—from financial services to public administration and beyond, here’s my honest take:

Clean architecture isn’t about folders. It’s about keeping your business logic independent from the frameworks and tools that deliver it.

You don’t need perfect hexagonal architecture on day one. You need to make sure that when requirements change (and they will), you can adapt without rewriting everything.

The confusion exists because we focus on the wrong things. We argue about package names while our domain models are covered in @Entity annotations. We create elaborate folder structures while our business logic lives inside Spring controllers.

Start simple. Keep your business rules clean. Test them without mocking the world. Everything else is just details.

Back to Blog

Related posts

Read more »

Java Virtual Threads — Quick Guide

Java Virtual Threads — Quick Guide Java 21+ · Spring Boot 3.2+ · Project Loom A concise, production‑focused guide to Java Virtual Threads — what they are, how...