The Birthday Greetings Kata by Matteo Vaccari is a good way to think about decoupled design and the “Hexagonal Architecture”.

Problem

I’ll quote directly from the original blogpost on the subject.

Problem: write a program that

  • Loads a set of employee records from a flat file
  • Sends a greetings email to all employees whose birthday is today

The flat file is a sequence of records, separated by newlines; this are the first few lines:

 last_name, first_name, date_of_birth, email
 Doe, John, 1982/10/08, john.doe@foobar.com
 Ann, Mary, 1975/09/11, mary.ann@foobar.com

The greetings email contains the following text:

Subject: Happy birthday!

Happy birthday, dear John!

with the first name of the employee substituted for “John”.

The program should be invoked by a main program like this one:

public static void main(String[] args) {
   ...
   BirthdayService birthdayService = new BirthdayService(
       employeeRepository, emailService);
   birthdayService.sendGreetings(today());
}

Note that the collaborators of the birthdayService objects are injected in it. Ideally domain code should never use the new operator. The new operator is called from outside the domain code, to set up an aggregate of objects that collaborate together.

Breaking the problem down.

Implementing the solution involves breaking the problem into various pieces that can be implemented, and then using those pieces to solve the problem.

The definition of the problem breaks down into two different components:

  • Load a set of employee records from a flat file

and

  • Send a greetings email to all employees whose birthday is today

Employee model

Based on the sample data provided, the Employee model is simple.

data class Employee(val lastName: String, val firstName: String, val dateOfBirth: LocalDate, val email: String)

These fields are taken directly from the sample CSV file.

Testing the employee birthday

Employees are simple in this problem, we really only need to know if any given date is “their birthday”, and tests for this and the implementation are fairly trivial.

internal class EmployeeTest {
    val employee = Employee("test", "test", LocalDate.of(1990, 2, 5), "test@example.com")

    @Test
    fun isBirthdayWithIncorrectDate() {
        assertFalse(employee.isBirthday(LocalDate.of(2019, 5, 9)))
    }

    @Test
    fun isBirthdayWithCorrectDate() {
        assertTrue(employee.isBirthday(LocalDate.of(2019, 2, 5)))
    }
}

The simple data class grows a single function…

data class Employee(val lastName: String, val firstName: String, val dateOfBirth: LocalDate, val email: String) {
    fun isBirthday(date: LocalDate): Boolean {
        return Pair(date.dayOfMonth, date.monthValue) == Pair(dateOfBirth.dayOfMonth, dateOfBirth.monthValue)
    }
}

The only interesting thing here is that it instiantiates two “tuples” (Pair) objects for comparisons.

Employee repository

Reading the specification, it’s clear that there’s a need for a way to get the list of employees, and the sample code makes reference to an employeeReference dependency.

In the absence of a better name, I’ll go with an EmployeeRepository interface.

interface EmployeeRepository {
    fun getAll(): Sequence<Employee>
}

I’ve opted to use a Sequence for the return type, read more about Kotlin’s sequences here for why, but the upshot is that they’re lazier than an Iterator, for this simple case it probably won’t make much of a performance difference, but Sequence seems more scalable.

import org.junit.Assert.assertEquals
import org.junit.Test

internal class FileEmployeeRepositoryTest {

    @Test
    fun `reading from a data file`() {
        val filename = fixturePath("fixtures/employee_data.txt")
        val repository = FileEmployeeRepository(filename)

        val employees = repository.getAll()
        assertEquals(2, employees.count())
    }

    @Test
    fun `parsing an Employee record from a data file`() {
        val filename = fixturePath("fixtures/employee_data.txt")
        val repository = FileEmployeeRepository(filename)

        val employees = repository.getAll()

        val employee = Employee(
            "Doe",
            "John",
            LocalDate.of(1982, 10, 8),
            "john.doe@foobar.com"
        )
        assertEquals(employee, employees.first())
    }

    private fun fixturePath(filename: String) =
            javaClass.classLoader.getResource(filename).path
}

The basic implementation looks like this…

class FileEmployeeRepository(val filename: String) : EmployeeRepository {
    private val formatter = DateTimeFormatter.ofPattern("y/M/d")

    override fun getAll(): Sequence<Employee> {
        return File(filename)
                .bufferedReader()
                .lineSequence()
                .drop(1)
                .map(this::parseEmployee)
    }

    private fun parseEmployee(line: String) : Employee {
        val (lastName, firstName, date, email) = line.split(",").map(String::trim)
        return Employee(lastName, firstName, LocalDate.parse(date, formatter), email)
    }
}

Looking first at getAll(), this is a functional implementation of the parsing.

  1. Open the file and create a “buffered reader”
  2. Create a lineSequence from the bufferedReader - this means that the parsing will be lazy.
  3. drop the first line (this is the header row in the file)
  4. apply (map) this::parseEmployee to each line and return the result.

And parseEmployee isn’t particularly complex.

val (lastName, firstName, date, email) = line.split(",").map(String::trim)

This splits the line on commas, and trims any whitespace from each of the items.

The other part, is using Kotlin’s destructuring to extract the elements.

The second line just instantiates an Employee with the various fields extracted.

So far, I’ve parsed a CSV file and instantiated Employee data records for each entry, in the next part, I’ll look at the email sending part of the problem.