5

I am trying to resolve a circular dependency between two components in my system. The Messenger component is responsible for sending and receiving messages on a web socket. The Controller component requires use of the Messenger component to be able to send messages to connected clients because the Messenger component knows about the connected sessions.

The Messenger component has a dependency on the Controller component to be able notify the controller of incoming messages.

The WebsocketHandler is a framework interface that I have no control over in this case and calls the appropriate methods on my implementation based on the connected clients.

What architectural changes could I make to resolve this?

UML Diagram

interface WebsocketHandler
{
    void onConnect(Session session);
    void onDisconnect(Session session);
    void recieve(Object payload);
}

class Messenger implements WebsocketHandler
{
    Controller controller;
    Set<WebsocketSession> websocketSessions;

    @Override
    void onConnect(Session session)
    {
        sessions.add(session);
    }

    @Override
    void onDisconnect(Session session)
    {
        sessions.remove(session);
    }

    @Override
    void recieve(Object payload)
    {
        controller.messageReceived(payload);
    }

    // requires access to sessions to send messages
    void send(Object payload)
    {
        for (WebsocketSession session : websocketSessions)
        {
            session.send(payload);
        }
    }
}

class Controller
{
    Messenger messenger;

    void messageReceived(Object payload)
    {
        // do something with payload
    }

    void notifyClients()
    {
        messenger.send(...);
    }
}
Robert Hunt
  • 159
  • 1
  • 1
  • 3

3 Answers3

6

The simple solution to this is to recognise that Controller doesn't need a dependency on Messenger, it needs a dependency on send. So provide it with that dependency:

interface Sender {
    void send(Object payload);
}

class Messenger implements WebsocketHandler, Sender { …

class Controller
{
    Sender sender;

    void messageReceived(Object payload)
    {
        // do something with payload
    }

    void notifyClients()
    {
        sender.send(...);
    }
}

Now, Controller just has a dependency on the Sender interface, removing that circular dependency.

UPDATE

As the OP points out, the above removes the direct circular dependency between Controller and Messenger. However, if constructor injection is used, the dependency still exists at the point of creation of the objects: we need to create Sender first to satisfy Controller's need for a Sender, but we need to create Controller first as Messenger needs to call controller.messageReceived(payload).

There are various ways of working around this, such as supporting property injection. But my personally favourite, for languages that support functions as first class citizens is to fully decouple the two objects via functions.

Such a solution, expressed as C# (as I'm more familiar with that language), might look like:

class Messenger : WebsocketHandler
{
    private readonly Action<object> _messageReceived;
    private readonly IEnumerable<WebsocketSession> _websocketSessions;

    public Messenger(Action<object> messageReceived) 
        => _messageReceived = messageReceived;


    public void Receive(object payload) => _messageReceived(payload);

    public void Send(Object payload)
    {
        foreach (var session in _websocketSessions)
        {
            session.Send(payload);
        }
    }
}

class Controller
{
    private readonly Action<object> _send;

    public Controller(Action<object> send) => _send = send;

    public void MessageReceived(object payload)
    {
        // do something with payload
    }

    public void NotifyClients() => _send(null);
}

And then the code to couple the two objects at runtime might be something like:

void Setup()
{
    Controller controller = null;
    var messenger = new Messenger(MessageReceiver);
    controller = new Controller(Sender);

    void MessageReceiver(object payload) => controller.MessageReceived(payload);
    void Sender(object payload) => messenger.Send(payload);
}

Java these days supports such an approach too. I do not know though how well this would work with IoC frameworks like Spring.

David Arno
  • 38,972
  • 9
  • 88
  • 121
  • This sounds great but what about the fact `Sender` needs access to the session held in `Messenger`. Would it make sense to extract them into some sort of session registry component? – Robert Hunt Aug 09 '18 at 19:10
  • @RobertHunt Which one? – Stop harming Monica Aug 09 '18 at 20:23
  • @Goyo the `Messenger` class currently stores a set of websocket sessions as clients connect and disconnect, for the `Sender` to send a message it needs access to these sessions. – Robert Hunt Aug 09 '18 at 21:35
  • 1
    Yes, but knowledge of that session is not part of the Sender interface. Breaking Sender Method signatuure breaks the circular dependency between Messenger and Controller and abstracts away implementation details of sendini a message.Session is an implementation detail that happega to be necessart for Messenger that implementation the Sender. At some future point in time you might replace implementation of Sender with one that sends its messages to Kafka topic for example and Controller will not care a one bit. – Roland Tepp Aug 10 '18 at 05:17
  • @RobertHunt, I'm sorry, I don't understand your question. `Messenger` implements `Sender`. It is the sender. So why would `Sender` need to know about the sessions? That's an implementation detail of `Messenger`. – David Arno Aug 10 '18 at 06:40
  • 1
    @DavidArno Sorry, I had missed the fact you had `Messenger` implementing the `Sender` interface, it makes sense now. – Robert Hunt Aug 10 '18 at 06:50
  • Maybe I'm misunderstanding things here, this solution resolves the circular dependencies at the code level but if I were to use a DI container to create these components such as Spring and use constructor injection it wouldn't physically be able to construct the components. To instantiate `Controller` I would still need to create a `Sender` (Messenger) but to create `Messenger` I need to create a `Controller`. Are these two different levels/concepts of "circular dependency", this post explains the same https://stackoverflow.com/questions/44029931/how-do-interfaces-solve-circular-dependencies – Robert Hunt Aug 10 '18 at 07:38
  • @RobertHunt, You are right: my basic solution just moves the circular dependency problem if constructor injection is used. I've updated my answer to address this point. – David Arno Aug 10 '18 at 09:23
1

The Messenger component has a dependency on the Controller component to be able notify the controller of incoming messages.

OMG java does not have events!!!

It does have several alternatives though. For example

https://docs.oracle.com/javase/6/docs/api/java/util/Observable.html

Here you would make your Messenger implement Observable and have the Controller call addObserver The Messenger can then notify the Controller of incoming messages with its notifyObservers() function without having to reference the Controller class.

There are several variations on the technique.

https://docs.oracle.com/javase/tutorial/uiswing/events/index.html

The Idea is that you pass in to the messenger the function to call when a message comes in rather than passing in the whole controller class.

Ewan
  • 70,664
  • 5
  • 76
  • 161
  • This is a classic example of "when you have a circular dependency, often there is a third object wanting to come out". In this case, the object is the observer (which turns out to be the same object, but seen through a different protocol). – Jörg W Mittag Aug 09 '18 at 15:52
0

In that particular case (when you need to notify someone about something), you can use an aggregator\messenger.

One of my favorite (from C# though) is this one: https://github.com/NimaAra/Easy.MessageHub

You create a message class, you subscribe your service, you fire the message from another service - that's it. Behind the scenes, aggregator just defines the list of subscribers, who should be notified for the particular message.

I'm pretty sure Java should have something similar.

Vitalii Vasylenko
  • 201
  • 1
  • 2
  • 8