diff --git a/_posts/2016-11-03-build-maintainable-systems-with-hexagonal-architecture.md b/_posts/2016-11-03-build-maintainable-systems-with-hexagonal-architecture.md index 095c8ec92bd1..8bb97def27e8 100644 --- a/_posts/2016-11-03-build-maintainable-systems-with-hexagonal-architecture.md +++ b/_posts/2016-11-03-build-maintainable-systems-with-hexagonal-architecture.md @@ -4,13 +4,13 @@ title: Build Maintainable Systems With Hexagonal Architecture author: ilu --- -![Hexagonal Architecture]({{ site.url }}{{ site.baseurl }}/assets/hexagonal-architecture.png) +![Hexagonal Architecture]({{ 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) +![Layers]({{ 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. @@ -18,15 +18,17 @@ This way of building applications can be considered simple and effective. But it 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. +The core alone is not 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 that drive the application we call the primary ports and the modules that use them are primary adapters. -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/). +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. + +The secondary ports may have one or more implementations. For example there may be a mock database for testing and a real database for running the application. The secondary port implementations are called secondary adapters. Here comes 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) +![Ports and adapters]({{ site.baseurl }}/assets/ports_and_adapters.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. +The diagram shows how the domain is in the middle surrounded by ports on sides of the 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. On the outer hexagon layer reside the primary and secondary adapters. 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. @@ -36,7 +38,7 @@ Next we will demonstrate Hexagonal Architecture by building a lottery system. Th 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) +![Lottery system]({{ site.baseurl }}/assets/lottery.png) ## Start from the core concepts @@ -61,17 +63,17 @@ The lottery players use `submitTicket()` to submit tickets for lottery round. Af `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 +## Primary adapter 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. +Now that the core implementation is ready we need to define the primary adapter for the players. We introduce `ConsoleLottery` class to provide command line interface that allows players to interact with the lottery system. It has commands to view and transfer bank account funds, submit and check lottery tickets. -## Primary port for the administrators +## Primary adapter for the administrators -We also need to define the lottery administrator facing port. This is another command line interface named `ConsoleAdministration`. +We also need to define the lottery administrator facing adapter. This is another command line interface named `ConsoleAdministration`. diff --git a/assets/ports_and_adapters.png b/assets/ports_and_adapters.png new file mode 100644 index 000000000000..d285045dea68 Binary files /dev/null and b/assets/ports_and_adapters.png differ diff --git a/cibuild.sh b/cibuild.sh index 86b2ee2e32da..d4e6e372e0fa 100755 --- a/cibuild.sh +++ b/cibuild.sh @@ -14,7 +14,16 @@ git log --pretty=format:'%H' -n 1 >> _config.yml bundle exec jekyll build -# - ignore everything below every webapp directory, so we dont mess with the source code +# credit: code snippet borrowed from jekyllrb.com website source +IGNORE_HREFS=$(ruby -e 'puts %w{ + example\.com.* + https:\/\/github.com\/iluwatar\/java-design-patterns\/fork + https:\/\/sonarqube.com.* +}.map{|h| "/#{h}/"}.join(",")') +# - ignore example.com because they are just examples/fakes # - ignore the fork link of our project, because it somehow is not valid (https://validator.w3.org/) +# - ignore sonarqube.com/api/badges/gate because of travis-only 'SSL Connect Error's + +# - ignore everything below every webapp directory, so we dont mess with the source code # - ignore the folder principles of the external dependency (git submodule) webpro/programming-principles -bundle exec htmlproofer ./_site/ --file-ignore "/.+\/(webapp|principles)\/.*/" --url-ignore "https://github.com/iluwatar/java-design-patterns/fork" +bundle exec htmlproofer ./_site/ --file-ignore "/.+\/(webapp|principles)\/.*/" --url-ignore $IGNORE_HREFS --check-html --allow-hash-href diff --git a/patterns b/patterns index 68ec24c62e4b..c94c8a3e7479 160000 --- a/patterns +++ b/patterns @@ -1 +1 @@ -Subproject commit 68ec24c62e4b7d637b27152af214afb55f571726 +Subproject commit c94c8a3e74799c5dc83bd90d98669ad697e1087b