"What goes inside the hexagon? I don't care." - Cockburn
My House is a scoring API for prioritizing families in an affordable housing program: it takes a batch of families (total income and dependents), scores each one against business rules, and returns the eligible families sorted by score. The point isn't the CRUD itself. It's using a small domain with real rules to put Hexagonal Architecture (Ports & Adapters) into practice end to end, keeping the business core 100% independent of Spring.
The key idea of Hexagonal Architecture is the dependency rule: everything points inward. The
domain doesn't know Spring, HTTP, or a database exist; the edges are what know about the domain. The outside
world talks to it through ports (interfaces defined by the domain), which infrastructure
adapters implement. A POST /families comes in through an HTTP adapter, passes
through a use case, and reaches the core; on the way out, persistence goes through a port, never a direct
dependency. In practice, swapping in-memory for Postgres, or REST for messaging, only touches the edges, and the
core stays intact.
Family, Dependent) and the
scoring rules are plain Java, with no Spring annotations and no infrastructure dependencies. The framework
stays at the edge.
RegisterFamiliesGateway,
ListFamiliesGateway) defined in the domain; persistence plugs in through adapters (in-memory for
now) that can be swapped for a real database without touching the core.
Scoring
interface, implemented by MyPopularHouseScoring. Changing the program's criteria means plugging
in another strategy, without touching use cases or entities.
UseCaseContract<I, O>): RegisterFamilies and
ListEligibleFamilies.
Filter built with a
Builder (sortBy, pageNumber, pageSize).
Family.create() (new family, score calculated on
construction) and Family.restore() (rehydration), preventing invalid states.
The rules themselves (income brackets, per-dependent bonus) are just what goes inside the hexagon,
deliberately simple and swappable via Strategy. What matters here is that they live in the domain,
with zero coupling to Spring, HTTP, or persistence.
POST /families: registers a batch of families with income and dependents.GET /families/eligibles: lists eligible families sorted by score (paginated).Family entity and the scoring strategy), without booting Spring.