Software Architecture & Design Principles

Use-Case-Driven Design

A codebase's top-level structure should announce what the system does, not which framework renders it — "screaming architecture." Organizing around use cases rather than technical layers keeps intent visible and framework choices genuinely swappable.
  • Screaming architecture: the top-level package layout should scream "shopping cart" or "loan origination," not "controllers/services/repositories"
  • A use case (interactor) is a first-class object: one class, one application-specific business operation, orchestrating entities to fulfill it
  • Package-by-feature over package-by-layer keeps everything one use case touches physically close together (raising cohesion, see Coupling And Cohesion)
  • Frameworks are details — a web framework is a delivery mechanism plugged into use cases, not the thing the architecture is organized around
  • This is what makes a system's intent legible to a new developer from the folder structure alone, before reading a line of logic
A use case as an explicit, testable object
public interface RegisterUser {
    Result execute(RegisterUserRequest request);
}

public class RegisterUserInteractor implements RegisterUser {
    private final UserRepository users;
    private final Notifier notifier;

    RegisterUserInteractor(UserRepository users, Notifier notifier) {
        this.users = users;
        this.notifier = notifier;
    }

    public Result execute(RegisterUserRequest request) {
        // application-specific business rule, orchestrating entities —
        // no HTTP, no SQL, no framework import in sight
        if (users.findByEmail(request.email()).isPresent()) return Result.duplicateEmail();
        User user = User.register(request.email(), request.password());
        users.save(user);
        notifier.send(user.welcomeMessage());
        return Result.success(user.id());
    }
}
Sources
  • Clean ArchitectureCh. 21 — Screaming Architecture