Have you ever heard of hexagonal architecture? In my experience, the question quite divides people. It's either yes! wow! I love it, I'm doing it / experimenting with it right now, OR no what? what even is that, is it Android based. hexa - six something?
Here I'm going to try to explain hexagonal architecture in its simplest form and frankly in the way I understand it so far. Formally described by Alistair Cockburn in 2005 (otherwise known as Ports & Adapters). I'm going to be framing the discussion from an Android perspective, and for those who answered no to the original question this should give you some grounding and a base for everyone to start to be involved in any hexagonal conversation.
Let's set some pessimistic knowns: apps and or activities or fragments can become big and unwieldy. Android testing is near impossible or a pain in the ass. Libraries are needed and our favourite libs engrained in our conscious, if we had to switch from Glide to Picasso it would take a week of work. Behaviour is spread across the application, a change request involves touching n classes in n packages.
Yes I'm being pessimistic but I'm sure you can relate to some part of that. I want to point out that the above does not have a golden bullet solution in hexagonal architecture and other software development strategies can help combat the code rot. Such things as front end architectures: MVP, MVC, MVVM, coding practices: SOLID, DRY, KISS, YAGNI, development practices: TDD, BDD, pairing, code reviews, clean code.
Hexagonal architecture would never come to fruition without combining with some of the above mentioned practices and it is important to re-iterate you need a diverse set of tools in your toolbox to be able to create a maintainable, testable, bug free, feature rich application.
With that said, hexagonal architecture is a selfish design pattern. It's about you sticking up for you and your business; letting others do as much work as they should. The trick here is being able to work out what is someone else's responsibility and what we should be doing ourselves.
For example, our latest app (Channel 4 Video On Demand) plays streams of video content, organises this content by date/alphanumeric/genre and shows current programs whether you are offline or online. This means it needs to pull data from the web, rearrange data as it sees fit and also store data for when it can't access the web. Now pulling data from the web involves http call execution but Channel 4 don't care about that! Sorting data - hell I'm sure Channel 4 want to make sure we get this right, they want their latest series shown first, also showing data still when offline is important but knowing that to do this data is stored in an sqlite database or a flat file system or serialised into shared preferences is not at all important to Channel 4 (and therefore to us).
This is the selfishness of hexaganol architecture, concentrate on what you are good at. Decouple your businesses domain from the Android framework, decouple what you care about from the implementation details of how it works.
What this means, is to separate your concerns, make sure your business logic and business rules around sorting, filtering, calculating is separate from the implementation of fetching, persisting, communicating.
We are experimenting with this on Android.
At Novoda we have found a great start to this is having at least two modules per application: one called
core and one called
Core is a Java only module that contains the business logic paramount to your company as discussed above. It will expose one or more interfaces (known as a port) that a component inside of the
mobile module will implement (known as an adapter). This is a one way relationship
mobile knows about
core but not the other way around. For example, Channel 4 want to request a video's details (programme name, title, duration) from the web. However your
core app doesn't care how this is requested and so declares an interface like
RequestExecutor. A class in
mobile will implement this and perhaps use RxJava, HttpConnection or OkHttp or X, it doesn't matter what it is to
core but it will get this job done.
The advantages of this type of setup include testing, simplicity, re-use and maintainability.
It is testable because our
core module is java only. The ports we rely on are just interfaces the implementation details are abstracted. This means we can test through the JVM with pure JUnit tests but more importantly, because the business requirements are implemented in one place (
core) we can surround these with tests that ensure when new functionality is added the business logic of already written features is not broken. Ports and Adapters help towards a separation of concerns which makes testing simpler and more modular.
It is simple because we are starting to split our app apart, like we said before hexagonal architecture can feed into and work well with other best practices. Following the Single Responsibility Principle of SOLID allows each module to take on a single responsibility. The
core is responsible for our business logic and how the application differs from everything else out there.
Mobile is left to implement the intricacies of the platform and is very much specialised to the domain the user will be using the app upon - rather than the domain of what the user will be viewing.
Re-use is improved with hexagonal architecture. The possibilities with creating such ports around your specialised domain logic means you could theoretically take the
core module and only need to create a new
Maintainability, this is a big big plus. Did you see what just happened with Parse having their kill switch flicked? If you had a hexagonal architecture, changing out Parse is just small implementation detail. The benefit is that you have your full domain tested and changing Parse means changing it only in one place and running the tests afterwards for sanity. Like we discussed before with a port for a
RequestExecutor and an adapter for a
FacebookParseRequestExecutor you can imagine easily being able to switch this out for a
FirebaseRequestExecutor or a
RetrofitRequestExecutor or to take it one step further a
MockRequestExecutor or a
LocalFileSystemRequestExecutor. Hopefully you can start to see some of the power ports and adapters / hexagonal architecture can give you.
core module is not spoken about in the hexagonal architecture pattern, on Android it really helps you to understand what is part of your applications domain and what is part for the platform. In keeping with expanding the pattern, Robert Martin has took this further and declared his clean architecture which for me has many crossovers with hexagonal architecture. It is great at explicitly saying what layers should have what responsibilities (our original problem). What is misses out is the notion of ports and adapters, therefore knowing both architectures is a real benefit.
I realise I've not given much implementation detail here, but that's on purpose - there are many ways to do this and hence why we are still experimenting. I want to start a discussion about pros, cons and possibilities, let's talk.