1Comprobadas vs. no comprobadas

Antes de diseñar la jerarquía, ten clara la diferencia: define si MultagalException obligará al llamador a capturarla o no.

AspectoComprobadas (checked)No comprobadas (unchecked)
SuperclaseException (salvo RuntimeException)RuntimeException
Obligación del compiladorCapturar o declarar con throwsOpcional
Caso típico en E/SIOException, NoSuchFileExceptionNullPointerException, IllegalArgumentException
Uso habitualErrores recuperables externosErrores de programación / de dominio ya traducidos

2Subtipos de dominio

Al menos una excepción para «no encontrado» y otra para fallos de almacenamiento — las que ya necesitan A1.1 y A1.2.

java
public class MultagalException extends RuntimeException {
    public MultagalException(String mensaje, Throwable causa) {
        super(mensaje, causa);
    }
}

public class ExpedienteNoEncontradoException extends MultagalException {
    public ExpedienteNoEncontradoException(String numero, Throwable causa) {
        super("No existe el expediente: " + numero, causa);
    }
}

public class AlmacenamientoException extends MultagalException {
    public AlmacenamientoException(String mensaje, Throwable causa) {
        super(mensaje, causa);
    }
}

3Traducir las excepciones de bajo nivel

Revisa el código de A1.1 (AlmacenExpedientes), A1.2 (ServicioPurgaExpedientes) y A1.3 (VigilanteBuzonEntrada): ningún método público debe dejar escapar IOException sin traducir.

java
public Optional<Path> buscar(String numeroExpediente) {
    try (Stream<Path> stream = Files.walk(raiz)) {
        return stream
                .filter(Files::isRegularFile)
                .filter(p -> coincide(p, numeroExpediente))
                .findFirst();
    } catch (IOException e) {
        throw new AlmacenamientoException(
                "Error de E/S al buscar el expediente " + numeroExpediente, e);
    }
}
Conserva siempre la causaPasa la excepción original como segundo parámetro (causa) al construir la excepción de dominio: sin ella se pierde la traza real del fallo en producción.

4try-with-resources en todo recurso AutoCloseable

Aprovecha la revisión para confirmar que todo Stream<Path>, WatchService u otro recurso cerrable de A1.1–A1.3 usa try-with-resources y no un finally manual.

java
// Varios recursos: se cierran en orden inverso, incluso si hay excepción
try (Stream<Path> stream = Files.walk(raiz);
     WatchService watcher = FileSystems.getDefault().newWatchService()) {
    // ...
}

5Diario técnico

Justifica por escrito por qué la raíz de la jerarquía es no comprobada.

!Problemas frecuentes

Perder la causa originalConstruir la excepción de dominio con solo un mensaje (new AlmacenamientoException("fallo")) sin pasar la IOException original como causa deja el stacktrace inútil para depurar en producción.
Capturar Exception a secasCapturar Exception en vez del tipo concreto (IOException, NoSuchFileException...) esconde bugs de programación bajo la misma traducción que los errores de E/S reales.

✓Criterios de aceptación

  • MultagalException extiende RuntimeException
  • Cada excepción de dominio conserva la excepción original como causa
  • Ningún método público de A1.1–A1.3 deja escapar IOException sin traducir
  • Todo recurso AutoCloseable usa try-with-resources
  • El diario técnico justifica por qué la raíz es no comprobada

Adaptado de apartado f — Excepciones y configuración y apartado h — Actividades.