Skip to content

Hexagonal blog #487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Nov 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c41c29f
Add some ideas about structure
iluwatar Apr 16, 2016
63947a0
Work on layered architecture description
iluwatar Apr 17, 2016
08bef33
Work on hexagonal architecture description
iluwatar Apr 17, 2016
d7f3087
Add some words
iluwatar Apr 19, 2016
06f858a
Work on lottery system description
iluwatar Apr 21, 2016
f0c0e6e
Fix spelling
iluwatar Aug 11, 2016
e203af0
Add content describing the implementation
iluwatar Aug 11, 2016
8b8d429
Added gist-it links
iluwatar Aug 11, 2016
75426c8
Fix image filename
iluwatar Aug 11, 2016
1c9194a
Add subheaders and some text
iluwatar Aug 11, 2016
90ff0f2
Update blog post date and add slice parameters for gist-it
iluwatar Aug 11, 2016
023e0e6
Add chapter about the test application
iluwatar Aug 11, 2016
77c80d9
Add console output
iluwatar Aug 11, 2016
a3911dc
Add note about testing layered architecture
iluwatar Aug 12, 2016
84350bf
Text improvements
iluwatar Aug 12, 2016
01edffe
Link to originals
iluwatar Aug 12, 2016
c364db6
Improve the text
iluwatar Aug 12, 2016
70f001e
Change blog title and shorten opening sentence
iluwatar Aug 13, 2016
986e1d9
Add lottery ticket image and some text
iluwatar Aug 13, 2016
137bead
Add missing image
iluwatar Aug 13, 2016
8cc0e52
Some changes based on review comments
iluwatar Aug 13, 2016
625c035
More changes based on review comments
iluwatar Aug 13, 2016
9855530
Improve administration port chapter
iluwatar Aug 13, 2016
e9eb45e
Improvements to banking chapter
iluwatar Aug 13, 2016
df5406f
Improve the notifications chapter
iluwatar Aug 13, 2016
3a795ba
Changes to database chapter
iluwatar Aug 13, 2016
1d169ed
Improve the app chapter
iluwatar Aug 13, 2016
6545030
Update images
iluwatar Aug 18, 2016
e51f5f1
Add some text
iluwatar Aug 18, 2016
1ceaa45
Replace title image
iluwatar Sep 3, 2016
d09d359
Text updated because of code refactoring
iluwatar Sep 4, 2016
732774f
Remove unused image
iluwatar Sep 4, 2016
c7d95c2
Hexagonal blog: Update text based on implementation
iluwatar Sep 15, 2016
429397b
Hexagonal blog: Small corrections
iluwatar Sep 17, 2016
c1604e4
Hexagonal blog: Update lottery image
iluwatar Nov 3, 2016
0785863
Hexagonal blog: Update blog post timestamp
iluwatar Nov 3, 2016
cca4034
Hexagonal blog: Update submodule
iluwatar Nov 3, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
layout: post
title: Build Maintainable Systems With Hexagonal Architecture
author: ilu
---

![Hexagonal Architecture]({{ site.url }}{{ site.baseurl }}/assets/hexagonal-architecture.png)

## The fallacies of layered architecture

This blog post is about implementing Alistair Cockburn's [Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture). To have something familiar to start with let's first talk about Layered Architecture. It is a well known architectural pattern that organizes application into layers each having their specific purpose. The database layer takes care of data transactions, the business layer is responsible for business logic and the presentation layer deals with the user input. The Layered Architecture implements so called separation of concerns principle which leads to more maintainable applications. Changes to one area in the software do not affect the other areas.

![Layers]({{ site.url }}{{ site.baseurl }}/assets/layers.png)

This way of building applications can be considered simple and effective. But it also has several drawbacks. When you see an application implemented with Layered Architecture, where is the application core? Is it the database? Maybe it's the business logic with some little things scattered over to the presentation layer. This is the typical problem with layers. There is no application core, there are just the layers and the core logic is scattered here and there. When the business logic starts to leak over to the presentation layer, the application can no longer be tested without the user interface.

## Core, ports and adapters

Hexagonal Architecture tackles this issue by building the application around the core. The main objective is to create fully testable systems that can be driven equally by users, programs and batch scripts in isolation of database.

However, the core alone may not be very useful. Something has to drive this application, call the business logic methods. It may be a HTTP request, automatic test or integration API. These interfaces we call the primary ports. Also, the core has its dependencies. For example, there may be a data storage module that the core calls upon to retrieve and update data. The interfaces of these modules that are driven by the core are called the secondary ports of the application.

Each of the ports may have one or more implementation. For example there may be a mock database for testing and a real database for running the application. The port implementations are called adapters. Thus the alias name Ports and Adapters for Hexagonal Architecture. Other architectural patterns describing the same concept are Uncle Bob's [Clean Architecture](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html) and Jeffrey Palermo's [Onion Architecture](http://jeffreypalermo.com/blog/the-onion-architecture-part-1/).

The name of the pattern comes from the following hexagonal drawing.

![Hexagon]({{ site.url }}{{ site.baseurl }}/assets/hexagon.png)

The diagram shows how the domain is in the middle surrounded by ports on each side of hexagon. The actual amount of ports does not have to be exactly six, it can be less or it can be more depending on the application needs.

Naked Objects design pattern is considered an implementation of Hexagonal Architecture. Naked Objects is utilized by [Apache Isis](https://isis.apache.org/) framework. User defines the domain objects and the framework automatically generates user interface and REST API around it.

## Lottery system

Next we will demonstrate Hexagonal Architecture by building a lottery system. The lottery system will provide two primary ports: One for the users to submit lottery tickets and another for system administrators to perform the draw.

The secondary ports consist of lottery ticket database, banking for wire transfers and event log for handling and storing lottery events. The resulting hexagon of the system can be seen in the following diagram.

![Lottery system]({{ site.url }}{{ site.baseurl }}/assets/lottery.png)

## Start from the core concepts

We start the implementation from the system core. First we need to identify the core concepts of the lottery system. Probably the most important one is the lottery ticket. In lottery ticket you are supposed to pick the numbers and provide your contact details. This leads us to write the following classes.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java?slice=24:44"></script>
<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java?slice=32:144"></script>

`LotteryTicket` contains `LotteryNumbers` and `PlayerDetails`. `LotteryNumbers` contains means to hold given numbers or generate random numbers and test the numbers for equality with another `LotteryNumbers` instance. [PlayerDetails](https://github.com/iluwatar/java-design-patterns/blob/master/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java) is a simple value object containing player's email address, bank account number and phone number.

## The core business logic

Now that we have the nouns presenting our core concepts we need to implement the core business logic that defines how the system works. In `LotteryAdministration` and `LotteryService` classes we write the methods that are needed by the lottery players and system administrators.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java?slice=31:"></script>

For administrators `LotteryAdministration` has `resetLottery()` method for starting a new lottery round. At this stage the players submit their lottery tickets into the database and when the time is due the administration calls `performLottery()` to draw the winning numbers and check each of the tickets for winnings.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java?slice=31:"></script>

The lottery players use `submitTicket()` to submit tickets for lottery round. After the draw has been performed `checkTicketForPrize()` tells the players whether they have won.

`LotteryAdministration` and `LotteryService` have dependencies to lottery ticket database, banking and event log ports. We use [Guice](https://github.com/google/guice) dependency injection framework to provide the correct implementation classes for each purpose. The core logic is tested in [LotteryTest](https://github.com/iluwatar/java-design-patterns/blob/master/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java).

## Primary port for the players

Now that the core implementation is ready we need to define the primary port for the players. We introduce `ConsoleLottery` class to provide command line interface that allows players to interact with the lottery system.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java?slice=41:"></script>

It has commands to view and transfer bank account funds, submit and check lottery tickets.

## Primary port for the administrators

We also need to define the lottery administrator facing port. This is another command line interface named `ConsoleAdministration`.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java?slice=35:"></script>

The interface's commands allow us to view submitted tickets, perform the lottery draw and reset the lottery ticket database.

## Secondary port for banking

Next we implement the secondary ports and adapters. The first one is the banking support that enables us to manipulate bank account funds. To explain the concept, the player can write his bank account number on the lottery ticket and in case the ticket wins the lottery system automatically wire transfers the funds.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java?slice=24:"></script>

The banking port has two adapters for different purposes. The first one `InMemoryBank` is a simple `HashMap` based implementation for testing. The lottery service's bank account is statically initialized to contain enough funds to pay the prizes in case some of the lottery tickets win.

The other adapter `MongoBank` is based on Mongo and is intended for production use. Running either one of the command line interfaces use this adapter.

## Secondary port for event log

Another secondary port is the lottery event log. Events are sent as the players submit their lottery tickets and when the draw is performed.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/LotteryEventLog.java?slice=26:"></script>

We have two adapters for this port: The first one `StdOutEventLog` is for testing and simply sends the events to standard output. The second `MongoEventLog` is more sophisticated, has persistent storage and is based on Mongo.

## Secondary port for database

The last secondary port is the database. It contains methods for storing and retrieving lottery tickets.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java?slice=30:"></script>

The port has two adapters. The `LotteryTicketInMemoryRepository` is a mock database holding its contents in memory only and is meant for testing. The `MongoTicketRepository` is used for production runs and provides persistent storage over application restarts.

## Lottery application

With all the pieces in place we create a command line application to drive the lottery system. The test application begins the lottery round using the administration methods and starts collecting lottery tickets from the players. Once all the lottery tickets have been submitted the lottery number draw is performed and all the submitted tickets are checked for wins.

<script src="http://gist-it.appspot.com/http://github.com/iluwatar/java-design-patterns/raw/master/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java?slice=62:"></script>

Running the test application produces the following output:

```
Lottery ticket for [email protected] was submitted. Bank account 265-748 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 024-653 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 842-404 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 663-765 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 225-946 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 023-638 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 983-322 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 934-734 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 735-964 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 425-907 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 225-946 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 265-748 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 946-384 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 425-907 was charged for 3 credits.
Lottery ticket for [email protected] could not be submitted because the credit transfer of 3 credits failed.
Lottery ticket for [email protected] was submitted. Bank account 833-836 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 284-936 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 335-886 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 310-992 was charged for 3 credits.
Lottery ticket for [email protected] was submitted. Bank account 234-987 was charged for 3 credits.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
Lottery ticket for [email protected] was checked and unfortunately did not win this time.
```

## Final words

Applications implemented with Hexagonal Architecture are a joy to maintain and work with. Implementation details such as frameworks, user interfaces and databases are pushed out of the core and the application can work without them. We can clearly point in the center of the hexagon and say that this is our application and it uses these technologies to implement the submodule interfaces. Restricting communication to happen only through the ports forces the application to produce testable and maintainable code.

The full demo application of Hexagonal Architecture is available in [Java Design Patterns](https://github.com/iluwatar/java-design-patterns) Github repository.
Binary file added assets/hexagon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/hexagonal-architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/layers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/lottery.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion patterns
Submodule patterns updated 454 files