# Error Handling and Resilience

## Overview

Error handling in this codebase is intentionally lightweight and centered on validation-driven recovery rather than exception-heavy control flow. In the owner/pet workflow, invalid input is detected early, reported through Spring's binding and validation infrastructure, and then recovered by returning the same form view with field-level errors preserved. This keeps the user experience predictable and makes the failure mode part of the normal request lifecycle.

The main pattern is: validate incoming form data, attach errors to the model, re-render the form when validation fails, and redirect only when the submission is valid. The relevant tests show this behavior from two angles: controller-level request handling in [`PetControllerTests`](src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java) and validator-level rule checking in [`PetValidatorTests`](src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java).

## Implementation Patterns

### Validation before persistence

The pet creation and update flows rely on validation as the primary resilience mechanism. Rather than letting bad input propagate into the domain model or repository layer, the controller tests show that invalid submissions are rejected and the same form is returned with errors attached to the `pet` model attribute.

Examples from [`PetControllerTests`](src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java):

- Blank names trigger a `required` error code on `pet.name`.
- Duplicate names trigger a `duplicate` error code on `pet.name`.
- Missing pet types trigger a `required` error code on `pet.type`.
- Invalid birth dates trigger type-mismatch errors on `pet.birthDate`.

These patterns are exercised in the nested test groups `ProcessCreationFormHasErrors` and `ProcessUpdateFormHasErrors`, which make the recovery path explicit: keep the user on `pets/createOrUpdatePetForm` and preserve the error state in the model.

### Controller-level recovery

The controller tests demonstrate the broader request flow:

- Successful submissions redirect to the owner detail page.
- Failed submissions do not redirect; they re-render the edit/create form.
- The `owner` model attribute remains error-free, while the `pet` attribute carries binding failures.

This separation is important because it shows that resilience is scoped to the failing object. The surrounding request context remains valid, and only the problematic form object is rejected.

### Validator-level rule enforcement

[`PetValidatorTests`](src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java) isolates the validation logic behind `PetValidator`. Instead of testing HTTP behavior, it uses a `MapBindingResult` and confirms that each invalid field produces a field error.

This gives the codebase two complementary safety nets:

- The validator ensures domain rules are enforced consistently.
- The controller ensures those failures are translated into a user-facing recovery path.

### Test-driven resilience

The test classes also act as executable documentation for resilience behavior. They encode the expected handling of:

- missing or malformed input,
- duplicate data,
- invalid object state,
- form re-display after failure,
- redirect-only-on-success behavior.

That makes it easier to extend the workflow without accidentally breaking error recovery.

## Key Classes

### [`PetController`](src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java)

Although the controller implementation is not shown here, the tests reveal its responsibility: orchestrating form submission, invoking validation, and deciding whether to redirect or redisplay the form. It is the main integration point where errors become user-visible.

### [`PetValidator`](src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java)

The validator encapsulates field-level rules for pets. The tests show that it is responsible for rejecting invalid name, type, and birth date values. This keeps validation logic out of controller code and makes it reusable.

### `ProcessCreationFormHasErrors`

This nested test class in [`PetControllerTests`](src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java) captures the creation-side failure cases. It is a useful indicator of how the application expects form submissions to fail safely during entity creation.

### `ProcessUpdateFormHasErrors`

This nested test class captures update-side failure cases. It shows that update handling follows the same recovery model as creation: fail in place, keep the user on the form, and report field-level problems.

### `ValidateHasErrors`

This nested test class in [`PetValidatorTests`](src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java) focuses on the validator contract itself. It proves the validator can detect and report each invalid field independently.

## How It Fits Together

```mermaid
flowchart TD
A["PetController"] --> B["PetValidator"]
B --> C["BindingResult errors"]
C --> D["pets/createOrUpdatePetForm"]
A --> E["redirect:/owners/{ownerId}"]
```

The flow is straightforward:

1. The controller receives a create or update request.
2. The validator checks the submitted `pet` data.
3. Validation problems are stored in a binding result.
4. If errors exist, the form is re-rendered with those errors.
5. If no errors exist, the request completes with a redirect.

This pattern gives the application a consistent recovery story: invalid input never becomes a partial success.

## Notes for Developers

- Keep validation logic in validators, not controllers, when the rule is reusable or domain-oriented.
- Prefer field-level errors with stable error codes so the UI can render precise feedback.
- Treat the form re-display path as part of the happy path for resilience; it is the intended response to invalid submissions.
- When adding new fields to pet forms, update both the validator and the controller tests so failure handling remains explicit.
- Preserve the distinction between creation and update tests, since both flows should fail safely in the same way.
- If you introduce broader exception handling later, keep it separate from validation recovery so request-level failures and business-rule failures remain easy to reason about.

