Having finally read Alistair Cockburn‘s article on Hexagonal architecture I see a great covariance to my understanding and application of our enterprise system. This is my take on the Ports and Adapter pattern and how I apply it in the context of the application server JBoss within the infrastructure of the product we happily sell.
The Needle Eye Pattern is useful whenever you have a subsystem to interact with, that is too painful to set-up within your unit tests. To some degree it’s a special case of the Gateway Pattern and the Facade.
By relying on conventions regarding the EJB2 system we’re using, we could generalize it so that we have a simple interface with a single method responsible for interacting with that other system.
Since we deal with services executed on a remote server, we called that interface the ServiceExecutor, with the single method
Input is a transfer object for the input parameters of the service, and
Output is the output object from the service. They’re a common base class. For example there is a
For EJB2 this consists on asking a locator for a remote object, getting the remote home, and calling the execute function with the particular input object, finally returning the output the remote service gets. Any exceptions thrown are passed, so that there is transparency in this regard to the calling client. The implementation makes use of reflection to get the particular homes, etc. as derived from the particular input class. As the name of the locator has to be “guessed” in this regard, it relies on a set of naming conventions for the package it’s put in.
The advantage is, that the remote system is completely decoupled from the business logic. We use it for our FIT and Slim test automation. By replacing the particular interface in our unit test for the test automation code with a mocked service executor we may influence the class under test without the need for a remote server at all.
The mechanism relies on having logged into the JBoss system beforehand and having provided the correct JNDI parameters. Of course the login is unnecessary when running in a unit test.
Since all interactions with the remote are handled via the ServiceExecutor, it becomes the needle eye of the classes. The drawback here is to be reliant on the naming scheme given by the particular implementation. For the pros, whenever our developers decide to switch to EJB3, we may simply provide an EJB3-capable implementation of our ServiceExecutor interface.
So far, we just used this for test automation related classes. Our system provides the capability for custom services and plugins, which then interact with the system itself, too. By providing a ServiceExecutor implementation, that calls local services within the same JBoss server, the services and plugins we become decoupled from the overall system. Thereby the business logic may be tested in a standalone manner without the use to start up the application server at all. By combining this approach with the Humble Object Pattern fast-running, truly in-memory unit tests for the business logic can be achieved. Tests using FIT, or Slim, or RobotFramework may then execute the business logic within the plugin code directly, by replacing the service executor with a mocked out interface. The only drawback here is that some end-to-end full system tests still need to be implemented, but not in the same level of detail. On the runtime behavior, the usage of reflection might result in a performance penalty, which could be avoided by using a map with the particular class names for the locators which are known, or by using a hash-approach on this.
- Ports and Adapter Pattern (aka Hexagonal Architecture)
- The Big Ball Of Mud
- The Gateway Pattern
- Humble Object Pattern
The variations part hasn’t been implemented thus far, but I think it would greatly payoff in regards to testability of our overall system. While reading through Growing Object-oriented Software – Guided by Tests I also found great value in combining our unit tests with the DSL type of unit tests using JMock. Our unit tests for our automation code are human readable and I was able to achieve a high degree of test coverage. One thing you have to keep in mind, is that you have to know the system your mocking out in this manner. Since it’s an internal system which I know to great detail, this was no problem after all.