« Reuse is overrated
What is a coach doing all the time? »
The birthday greetings kataPurposeTo learn about the hexagonal architecture, which is a good way to structure an application, and how to shield your domain model from external apis and systems. PrerequisitesThis is not a basic exercise. I suppose you are already familiar with TDD and refactoring. You will need a computer with Java ≥ 1.5 and a Java IDE (I assume you will use Eclipse). IngredientsThis kata can be done in two ways; if you want to try the refactoring way, then import in Eclipse the base code (update: the up-to-date version of the exercise code is on Github). If you want to try the TDD way, start with a blank Java project. Problem: write a program that
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 GoalsThe goal of this exercise is to come up with a solution that is
An optional complicationIf you want to develop further the domain logic, you can take into account the special rule for people born on a 29th of February: they should be sent greetings on the 28th of February, except in leap years, when they will get their greetings on the 29th. TestabilityA test is not a unit test if:
Integration tests have their place; but they should be clearly marked as such, so that we can execute them separately. The reason we draw this sharp distinction is that unit tests should be
One way to make code more testable is to use Dependency Injection. This means that an object should never instantiate its collaborator by calling the
The hexagonal architectureThe traditional way to structure an application in layers is +--------------+ | presentation | |--------------| | domain | |--------------| | persistence | +--------------+ The meaning of a layer diagram is that
In other words, it should be possible to compile a layer without access to the source code of the layers above it, as long as we have the source code of the layers below. The traditional three-layers architecture has many drawbacks.
The hexagonal architecture avoids these problems by treating all external systems as equally external. The system is not seen as a pipe with user interface as one end, and the database at the other. We model the system as a kernel of application code, surrounded by ports and adapters to the external systems.
Every external systems is hidden behind a facade that:
The domain model does not depend on any other layer; all other layers depend on the domain model. +-----+-------------+----------+ | gui | file system | database | |-----+-------------+----------+ | domain | |------------------------------+ How can we make the domain independent, for instance, of the database? We should define a repository interface that returns domain objects. The interface is defined in the domain layer, and is implemented in the database layer. The Facade-Adapter comboThe way to implement the hexagonal architecture is to abstract external systems and APIs with a Facade. A facade is a simplified view of the external system. For instance, there are a million of things that I could do with SQL and JDBC and my relational database. But my application only needs to do a few, specific things with the DB; for instance, retrieve all the employees that match some criterion. In that case, I can write a simple interface that exposes just that operation: interface EmployeeRepository { List<Employee> findEmployeesBornOn(int month, int day); } Note that the interface is written in terms of domain objects: the Employee object belongs to the domain. A case could be made that the specification for the date (month, day) should also be a domain object; I leave that to you, the reader. So that was the facade part. The domain logic will only deal with the facade, and can be tested thoroughly using stubbed and mocked versions of that interface. But what about the real implementation?
The code that talks with the real database (or flat file, in our case) implements the facade and correspond to the adapter pattern. The adapter can be unit tested as well, by mocking the external APIs, but this is usually not worth the effort. Mocking the JDBC APIs is in most cases very complicated. It’s perhaps more effective to just test it on a copy of the real database, or maybe with an in-memory version of the real database. There are no hard-and-fast rules here; there is a mixed bag of tricks. One thing most proponents of mock objects agree on is that you should only mock your own interfaces (see section 4.1 of Mock Roles, Not Objects). AcknowledgmentThe idea for the exercise of sending email I got from someone I overheard at an XP Day; I think it was Willem van den Ende but I’m not sure. Thanks to the Orione team for beta-testing and commenting on this kata, in particular Marco Gulino and Roberto Albertini who performed it more than once. ReferencesThe standard layered architecture is described in Patterns of Enterprise Application Architecture by Martin Fowler, and in Domain Driven Design by Eric Evans. The Hexagonal architecture is due to Alistair Cockburn. My collection of resources on the Hexagonal Architecture A pattern similar to the hexagonal architecture is the Onion architecture by Jeffrey Palermo. Miško Hevery explains the adapter-facade combo in his post Interfacing with hard-to-test third party code. Again Miško Hevery talks on Google Video on Dependency Injection, part of the Clean Code Talks Series.
Test Driven by Lasse Koskela contains plenty of tricks about how to do integration tests in Java. Appendix: Useful snippetsHow to convert a String to an InputStream: new ByteArrayInputStream(string.getBytes()); Update 2009/01/17: restored images, added CC license. This entry was posted on 13 January 2009 at 17:13 and is filed under Agile. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site. 8 Responses to “The birthday greetings kata”
Leave a Reply
|