chapter four

4 The honest hexagon: Use cases, commands, and the case against exceptions

 

This chapter covers

  • Defining use cases as typed input port contracts using domain language
  • Why exceptions as the sole error channel leave your use case contracts incomplete
  • Replacing exception-based error handling with Either<Error, R> for compiler-enforced, explicit failure paths
  • Structuring input as self-validating commands and queries
  • Routing use cases through a command and query bus
  • A first look at domain events as a mechanism for decoupled side effects

We built the core of WriteFlow's domain: entities that guard their own invariants, value objects that make illegal states unrepresentable, and factories that ensure objects enter the world valid. The domain is honest—it cannot be constructed into an invalid state, it says what it means, and it hides what it does not want touched. Now we wire that domain to the outside world.

This chapter is about the application layer—the thin ring that sits between external actors and your domain, orchestrating use cases without owning business logic. It is thinner than most developers expect; more powerful than most codebases reveal, though.

4.1 The use case contract

4.1.1 Input ports: The hexagon's public API

4.1.2 The anatomy of a use case

4.2 The incomplete contract

4.2.1 The cost of using exceptions for expected failures

4.2.2 The hidden cost in a hexagonal architecture

4.2.3 A deliberately provocative question

4.3 Either: An honest signature

4.3.1 What Either says that the void did not

4.3.2 The Error hierarchy

4.3.3 Returning multiple validation failures at once

4.3.4 Commands and queries as typed input

4.4 The Application handler

4.4.1 From use case interface to handler implementation

4.4.2 Orchestration without business logic leakage

4.4.3 Cross-cutting concerns at the boundary

4.5 The command bus

4.5.1 Why a bus? From one handler to many

4.5.2 The Command Bus in the Architecture Kernel

4.5.3 Spring wiring: Handlers as beans

4.5.4 Decorating the bus: Logging and metrics

4.6 The query bus

4.6.1 Queries are not commands

4.6.2 QueryHandler and QueryBus

4.6.3 CQS in practice

4.7 Domain events: A first look

4.7.1 When the primary task is done, then what?

4.7.2 Generating events in the domain, publishing in the application layer

4.7.3 Forward: The full event story