rgoussu@goussu: ~/library/applicative-architecture/exercises
~/library/applicative-architecture/exercises cat birthday-greetings-hexagonal.md

Birthday Greetings — hexagonal extraction

# Matteo Vaccari's Birthday Greetings kata as a hexagonal-architecture project — extract a pure domain core from a coupled script, put ports at the edges, then swap adapters to prove the boundary.

Exercisesaved 2026-08-08source #exercise#hexagonal-architecture#ports-and-adapters#tdd#design

Goal

Start from a small, deliberately coupled program — read employees from a CSV, email everyone whose birthday is today — and refactor it into a hexagon: a pure domain core behind ports, infrastructure in adapters. The program is trivial on purpose; the skill is drawing the boundary and proving it holds when the technology changes.

Subject: full brief & instructions

Practices

  • Hexagonal architecture — the whole exercise: ports, adapters, the dependency rule.
  • Coupling & cohesion — the extraction is the act itself: cut the coupling, regroup by cohesion, keep the dependency pointing inward.
  • TDD — outside-in with an acceptance test at the edges, fast unit tests on the core.
  • Testing strategies — the test strategy that falls out of the architecture: unit-test the core, contract-test the ports, integration-test the adapters.
  • Design patterns — Adapter doing the job it was named for.

Milestones

  1. Naive build. Implement the flat version (or take the kata's starting code): one class that reads employees.csv, finds today's birthdays, and sends greetings via a mail library. Ugly is fine — it's the raw material.
  2. End-to-end safety net. One acceptance test that pins current behavior at the system edges: a fixture CSV in, a fake SMTP server (or captured output) out.
  3. Find the domain. Separate the pure logic — "who has a birthday on date X" and the greeting content — from all I/O. No interfaces yet; just untangle.
  4. Driven ports. Extract EmployeeRepository and GreetingSender interfaces owned by the core; the CSV reader and the mail sender become adapters implementing them. The core now imports nothing from infrastructure.
  5. Fast tests. Unit-test the core against in-memory fakes; write one contract test per port that both the real adapter and the fake must pass.
  6. Prove the boundary. Add a second adapter pair — SQLite repository, console or Slack sender — selected purely by wiring. Zero changes in the core, or milestone 4 wasn't honest.

Stretch goals

  • Property-test the birthday rule, including Feb 29 birthdays in non-leap years.
  • Add a driving port: the same use case triggered by a CLI adapter and by a scheduler adapter.
  • Port the core to another language, carrying the unit tests over; only adapters should need rethinking.

Related