callbacks break hexagonal architectures

I just figured out why callback style APIs are harder to test (and harder to deal with in general). It’s because they break one of the essential ingredients of a hexagonal architecture: the middle hexagon should be independent of the adapters. Here’s why…

In order to use a callback API, one or more of our application classes must implement the callback method(s), and must therefore conform to some abstraction defined by the API’s provider. So our classes must depend on the API. Which means that the API can’t be easily mocked or stubbed. We have to treat our callback objects as being part of the adapter for that API, and test the rest of the application by mocking or stubbing them.

In fact, now I’ve written it down it’s trivially obvious, and hardly worth saying.

As you were.

Advertisements

7 thoughts on “callbacks break hexagonal architectures

  1. Hi Kevin,

    I’m afraid I don’t follow your logic when you say, “So our classes must depend on the API. Which means that the API can’t be easily mocked or stubbed.”

    Sure, to make yourself available for callback in a static language you’d have to implement an interface of some sort, but I don’t see why that makes it hard to test. In fact, I’ve always found callback APIs easier to unit test, since you can pass a test implementation into the call-back API to test that, and you can just call the callback method on the implementee without the need to touch the callback API.

    I did notice as I was writing this, that I kept thinking of a callback interface with only a single method on it. I don’t like callback APIs where you have to implement multiple methods since it is a) more complex and b) leaves you to deal with the vagaries of call order dependency.

    Objective C and Cocoa make extensive use of callbacks, but there they even forgo the need to implement an interface. The Cocoa APIs will check if you implement a method with a certain signature and, if you do, they’ll call it. This makes me very nervous, but seems to work out extremely well.

  2. Hi Rob,
    Should have taken more time…
    I was thinking of cases in which there are lots of callback methods to implement, probably with an implied calling sequence. So the client (ie. callee) is effectively a plug-in to a Template Method over which one has little or no control. Yes, the callee’s methods can often be tested individually. And for best results I like to ensure that the callee depends on the rest of the app, and not the other way around.

  3. I am reading this post 10 x time and still having problems to follow it.
    Can you confirm i am on a right track:
    callee (the class in middle hexagon) is to implement callback interface. This callback interface is defined by external entity (in hexagonal architecture this external entity is called Adapter).It all makes middle hexagon dependent on Adapter.
    “Which means that the API can’t be easily mocked or stubbed.” if my understanding above is correct how can you mock or stub callback API? For me it is impossible (or at least makes no sense) to stub/mock it. You just implement callback API and that’s all. In runtime your application will never call callback API implementation yourself, Adapter will do it instead.

    “and test the rest of the application by mocking or stubbing them.” – i do not get it

    • I messed up when trying to explain this idea — sorry! The idea is to use hexagonal architecture to “correct” the problematic dependencies created by a callback API…

      The external API is outside of our system, so (if we are to have a hex arch) we need to write an adapter for that API. The adapter will include one or more callees, objects that will define callback methods that are called (from outside our system) by the API. And while handling those callbacks our callee/adapter object will make calls on business objects in the middle hexagon.

      The callee/adapter thus isolates the system from the API and inverts the callback dependency. But all of this is at the expense of creating something (the callee) that’s hard to test.

  4. Hi

    Thanks for an answer. Now everything is clear: callee is part of adapter, its callback methods are called by external system, callback methods make call on middle haxagon objects => hexagonal architecture is preserved.

    I really wanted to have this clear since in my company we are thinking of creating report generation module and i am thinking of employing hexagonal architecture for this case.
    We are thinking of using callback for a specific case (i describe below) but still conform our design to hexagonal architecture.

    Currently, we are at the phase of designing API that the report generation module will expose to its clients.
    One of our requirements is to have report generated asynchronously and have the report generation module to notify its client that report was just generated. The solution i am thinking of is to introduce callback. This way client would provide callback implementation to report generation module (the API call like addListener(IReportCallBackSystem rcs) ) and after sucesful/failure of report generation the internals of report generation module would execute the callback method.

    The key to note is that this callback would be(at least i think it is ) completely different to the one you wrote about in this post. It wouldn’t be a part of an external system API but instead it would represent a way to notify interested parties about result of report generation.
    Moreover, the callback API (IReportCallBackSystem ) itself would be independent on report generation module and report generation module clients so in my opinion it would conform to “abstraction” according to Uncle Bob’s Dependency Inversion Principle and that’s why my callback would belong to middle hexagon. I hope it makes sense.

  5. Hi,

    Can you clarify this through a concrete example? E.g. the application receives emails via SMTP from an outer domain. The thirdparty API defiens the callback interface, called SmtpMessageListener. Then I would implement an adapter as follows:

    MyHexagonalAdapter implements SmtpMessageListener {
    [..]
    public void from(String sender) {
    this.sender = sender;
    }
    public void to(String recipient) {
    this.recipients.add(recipient);
    }
    public void data(InputStream stream) {
    myDomainSpecificListener.incomingMessage(
    Message
    .from(sender)
    .to(recipients)
    .content(consumeStream(stream));
    }
    }

    This would talk to my domain through domain specific messages, instead of SMTP specific messages.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s