Eleven exception classes, all extending one application root, and no intermediate types — which turned out to be right.
interface DomainException extends Throwable {}
final class OrderNotFound extends RuntimeException implements DomainException {}
final class StockUnavailable extends RuntimeException implements DomainException {}
// what we did NOT build:
// NotFoundException, ConflictException, ValidationException
// as intermediate classes for every domain error to
// extend, which is where hierarchies come from.
An intermediate class exists so a caller can catch a category, and every category we invented was really an HTTP status — which belongs in the layer that speaks HTTP, mapped from the concrete type. A flat set with a marker interface means a handler maps eleven types explicitly, which is more lines and no guessing.