A hexagonal  architecture simplifies deferring or changing technology decisions. You  want to change to a different framework? Write a new adapter. You want  to use a database, instead of storing data in files? Again, write an  adapter for it.

Draw a boundary around the business logic. The hexagon. Anything inside the hexagon must be free from technology concerns.
The  outside of the hexagon talks with the inside only by using interfaces,  called ports. Same the other way around. By changing the implementation  of a port, you change the technology.

Isolating  business logic inside the hexagon has another benefit. It enables  writing fast, stable tests for the business logic. They do not depend on  web technology to drive them, for example.

Here’s  an example diagram. It shows Spring MVC technology as boxes with dotted  lines, ports and adapters as solid boxes, and the hexagon without its  internals:

An adapter translates between a specific technology and a technology free port. The SpringMvcDriver adapter on the left forwards commands to the IReactToCommands port. Because the adapter actively uses the port, it's called a driver adapter. IReactToCommands is called a driver port. Its implementation is inside the hexagon. It's not shown on the diagram.

On the right side, the SpringMvcPublisher adapter implements the IWriteLines port. This time, the hexagon calls the adapter through the port. That's why SpringMvcPublisher is called a driven adapter. And IWriteLines is called a driven port.

I show you how to implement that application. We go all the way from a  user story to a domain model inside the hexagon. We start with a simple  version of the application that prints to the console. Then we switch  to Spring Boot and Spring MVC.

From a user story to ports & adapters

The company FooBars.io decides to build a Poetry App. The product owner and the developers agree on the following user story:

As a reader
I want to read at least one poem each day
So that I thrive as a human being

As acceptance criteria, the team agrees on:

  • When the user asks for a poem in a specific language, the system displays a random poem in that language in the console
  • It's ok to "simulate" the user at first, i.e. no real user interaction. (This will change in future versions.)
  • Supported languages: English, German

The developers meet and draw the following diagram:

poem-hexagon

So the SimulatedUser sends commands to the IReactToCommands port. It asks for poems in English and German. Here's the code, it's available on Github.

poem/simple/driver_adapter/SimulatedUser.java

public class SimulatedUser {
    private IReactToCommands driverPort;

    public SimulatedUser(IReactToCommands driverPort) {
        this.driverPort = driverPort;
    }

    public void run() {
        driverPort.reactTo(new AskForPoem("en"));
        driverPort.reactTo(new AskForPoem("de"));
    }
}

The IReactToCommands port has only one method to receive any kind of command.

poem/boundary/driver_port/IReactToCommands.java

public interface IReactToCommands{
    void reactTo(Object command);
}

AskForPoem is the command. Instances are simple, immutable POJOs. They carry the language of the requested poem.

poem/command/AskForPoem.java

public class AskForPoem {
    private String language;

    public AskForPoem(String language) {
        this.language = language;
    }

    public String getLanguage() {
        return language;
    }
}

And that's it for the left, driver side of the hexagon. On to the right, driven side.

poem-hexagon: driven side up next

When the SimulatedUser asks the IReactToCommands port for a poem, the hexagon:

  1. Contacts the IObtainPoems port for a collection of poems
  2. Picks a random poem from the collection
  3. Tells the IWriteLines port to write the poem to the output device

You can't see Step 2 yet. It happens inside the hexagon, in the  domain model. That's the business logic of the example. So we focus on  Step 1 and Step 3 first.

In Step 1, the collection of poems is a language dependent, hard coded array. It's provided by the HardcodedPoemLibrary adapter that implements the IObtainPoems port.

poem/boundary/driven_port/IObtainPoems.java

public interface IObtainPoems {
    String[] getMePoems(String language);
}

poem/simple/driven_adapter/HardcodedPoemLibrary.java

public class HardcodedPoemLibrary implements IObtainPoems {
    public String[] getMePoems(String language) {
        if ("de".equals(language)) {
            return new String[] { /* Omitted for brevity */ };
        } else { 
            return new String[] { /* Omitted for brevity */ };
        }
    }
}

In Step 3, the ConsoleWriter adapter writes the lines of the poems to the output device, i.e. the console.

poem/boundary/driven_port/IWriteLines.java

public interface IWriteLines {
    void writeLines(String[] strings);
}

poem/simple/driven_adapter/ConsoleWriter.java

public class ConsoleWriter implements IWriteLines {
    public void writeLines(String[] lines) {
        Objects.requireNonNull(lines);
        for (String line : lines) {
            System.out.println(line);
        }
        System.out.println("");
    }
}

We have created all the ports, and a simple implementation of all the  adapters. So far, the inside of the hexagon remained a mystery. It's up  next.

poem-hexagon: inside up next

Command handlers (inside the hexagon)

When a user asks for a poem, the system displays a random poem.
Similar in the code: when the IReactToCommands port receives an AskForPoemcommand, the hexagon calls a DisplayRandomPoem command handler.

The DisplayRandomPoem command handler obtains a list of  poems, picks a random one and writes it to the output device. This is  exactly the list of steps we talked about in the last clause.

poem/boundary/internal/command_handler/DisplayRandomPoem.java

public class DisplayRandomPoem implements Consumer<AskForPoem> {
        /* Omitted for brevity */

    @Override
    public void accept(AskForPoem askForPoem) {
        List<Poem> poems = obtainPoems(askForPoem);
        Optional<Poem> poem = pickRandomPoem(poems);
        writeLines(poem);   
    }

        /* Rest of class omitted for brevity */
}  

It's also the job of the command handler to translate between the domain model data and the data used in the port interfaces.

Tying commands to command handlers

In my implementation of a hexagonal architecture, there is only a single driver port, IReactToCommands. It reacts to all types of commands.

public interface IReactToCommands{
    void reactTo(Object command);
}

The Boundary class is the implementation of the IReactToCommands port. It creates a use case model using a library. The use case model maps each command type to a command handler. A ModelRunner runs the model and dispatches commands based on the use case model.

poem/boundary/Boundary.java

poem/boundary/Boundary.java

public class Boundary implements IReactToCommands {

    private ModelRunner modelRunner;

    public Boundary(IObtainPoems poemObtainer, IWriteLines lineWriter) {
        Model model = buildModel(poemObtainer, lineWriter);
        modelRunner = new ModelRunner().run(model);
    }

    private Model buildModel(IObtainPoems poemObtainer, IWriteLines lineWriter) {
        // Create the command handler(s)
        DisplayRandomPoem displayRandomPoem = new DisplayRandomPoem(poemObtainer, lineWriter);

        // Inject command handler(s) into use case model, to tie them to command
        // types.
        Model model = UseCaseModel.build(displayRandomPoem);
        return model;
    }

    @Override
    public void reactTo(Object commandObject) {
        modelRunner.reactTo(commandObject);
    }
}

Here's the use case model that defines the mapping between command  types and command handlers. The unconventional naming makes the model  easy to read. It stays close to the original acceptance criteria.

poem/boundary/UseCaseModel.java

class UseCaseModel {
    private static final Class<AskForPoem> asksForPoem = AskForPoem.class;

    public static Model build(Consumer<AskForPoem> displaysRandomPoem) {
        Model model = Model.builder()
            .user(asksForPoem).system(displaysRandomPoem)
        .build();

        return model;
    }
}

The use of the standard Java Consumer interface enables you to switch the command handler, without needing to create an extra interface for it.

The domain model

The domain model of the example doesn’t have very interesting functionality. The RandomPoemPicker picks a random poem from a list.

A Poem has a constructor that takes a String containing line separators, and splits it into verses.

The really interesting bit about the example domain model: it doesn’t  refer to a database or any other technology, not even by interface!

That means that you can test the domain model with plain unit tests. You don’t need to mock anything.

Such a pure domain model is not a necessary property of an  application implementing a hexagonal architecture. But I like the  decoupling and testability it provides.

Plug adapters into ports, and that's it

A final step remains to make the application work. The application  needs a main class that creates the driven adapters. It injects them  into the boundary.
It then creates the driver adapter,  for the boundary, and runs it.

poem/simple/Main.java

public class Main {
    public static void main(String[] args) {
        new Main().startApplication();
    }

    private void startApplication() {
        // Instantiate driven, right-side adapters
        HardcodedPoemLibrary poemLibrary = new HardcodedPoemLibrary();
        ConsoleWriter consoleWriter = new ConsoleWriter();

        // Inject driven adapters into boundary
        Boundary boundary = new Boundary(poemLibrary, consoleWriter);

        // Start the driver adapter for the application
        new SimulatedUser(boundary).run();
    }
}

And that's it! The team shows the result to the product owner. And  she's happy with the progress. Time for a little celebration.

hexagon-poem the completed application

Switching to Spring

The team decides to turn the poem app into a web application. And to  store poems in a real database. They agree to use the Spring framework  to implement it.
Before they start coding, the team meets and draws the following diagram:

poem-springboot hexagon

Instead of a SimulatedUser, there is a SpringMvcDriver now. The Spring MVC PoemController uses it to forward commands to the hexagon.

poem/springboot/PoemController.java

@Controller
class PoemController { 
    private SpringMvcDriver springMvcDriver;

    @Autowired
    public PoemController(SpringMvcDriver springMvcDriver) {
        this.springMvcDriver = springMvcDriver;
    }

    @GetMapping("/askForPoem")
    public String askForPoem(@RequestParam(name = "lang", required = false, defaultValue = "en") String language, Model webModel) {
        // Forward commands to the hexagon, by using SpringMvcDriver
        springMvcDriver.reactTo(new AskForPoem(language), webModel);
        return "poemView";
    }
}

When receiving a command, the SpringMvcDriver informs the SpringMvcPublisher about the current MVC model.
Then, the SpringMvcDriver forwards the command to the driver port. (This triggers the command handler in the hexagon.)

poem/springboot/driver_adapter/SpringMvcDriver.java

public class SpringMvcDriver{
    private IReactToCommands driverPort;
    private SpringMvcPublisher springMvcPublisher;

    public SpringMvcDriver(IReactToCommands driverPort, SpringMvcPublisher springMvcPublisher) {
        this.driverPort = driverPort;
        this.springMvcPublisher = springMvcPublisher;
    }

    public void reactTo(Object command, Model webModel) {
        // Inform the publisher about the current Spring MVC model,
        // so that it can add attributes to it later.
        springMvcPublisher.setWebModel(webModel);

        // Forward the command to the hexagon driver port.
        driverPort.reactTo(command);
    }
}

On the right side of the hexagon, the SpringMvcPublisher adds an attribute lines to the Spring MVC model. That's the value Thymeleaf uses to insert the lines into the web page.

poem/springboot/driven_adapter/SpringMvcPublisher.java

public class SpringMvcPublisher implements IWriteLines {
    private static final String LINES_ATTRIBUTE = "lines";

    private Model webModel;

    public void setWebModel(Model webModel) {
        Objects.requireNonNull(webModel);
        this.webModel = webModel;
    }

    public void writeLines(String[] lines) {
        Objects.requireNonNull(lines);
        webModel.addAttribute(LINES_ATTRIBUTE, lines);
    }

        /* Rest of class omitted for brevity */
}

The team also implements a PoemRepositoryAdapter to access the PoemRepository. The adapter gets the Poem objects from the database. It returns the texts of all poems as a String array.

poem/springboot/driven_adapter/PoemRepositoryAdapter.java

public class PoemRepositoryAdapter implements IObtainPoems {
    private PoemRepository poemRepository;

    public PoemRepositoryAdapter(PoemRepository poemRepository) {
        this.poemRepository = poemRepository;
    }

    @Override
    public String[] getMePoems(String language) {
        Collection<Poem> poems = poemRepository.findByLanguage(language);
        final String[] poemsArray = poems.stream()
            .map(p -> p.getText())
            .collect(Collectors.toList())
            .toArray(new String[0]);
        return poemsArray;
    }
}

Conclusion

There are many ways to implement a hexagonal architecture. I showed  you a straightforward approach that provides an easy to use, command  driven API for the hexagon. It reduces the number of interfaces you need  to implement. And it leads to a pure domain model.

If you want to get more information on the topic, read Alistair Cockburn’s original article on the subject. I also recommend reading this article.

The example in this article is inspired by a three part series of talks by Alistair Cockburn on the subject.

If you have a question, let me know. If you want to keep up with what I'm doing, follow me on dev.to, LinkedIn or twitter.

This article was first published on dev.to.