I currently have this Bot Viewer application

enter image description here

Which consist of the application itself - the Bot overviewer, which depends on a library Bot.Core.

Within Bot.core we have defined the core interface for each bot, and within this libary also implemented them as Bot x, Bot Z, Bot y.

Since Bot.Overviewer depends on the Bot.Core library, it can also see the different implementation, and thereby register them => add it to its scope.

I now want to to separate it to something like this

enter image description here

Where each bot implementation is separated into its own application. This causes the issue of how do I then register the different implementations of the bots?

Since Bot.Core - don't know who is implementing its interface, and bot Overviewer cannot not see it either.

How do I make Bot.Overviewer aware of those who has implemented the Bot.core interface?

  • Bot X, Bot Y, Bot Z and Bot Overviewer are 4 separate applications? Then you’ll need some sort of shared infrastructure.
    – Rik D
    Commented May 7, 2021 at 8:48

4 Answers 4


Decoupling using interfaces

The challenge in your design, is to have "independent" bots and an "independent" viewer and being able to connect them without coupling them.

First things first: the key constraint is that the Bot.Overviewer interacts with a bot only through the BotCore interface:

  • The viewer should not need to know which bot implementation is behind the interface.
  • If it is important to know something about a bot (e.g. to inform an operator in the viewer's user interface), this knowledge must be obtainable via the interface (e.g. a method getBotName())
  • The viewer should not use any bot implementation-specific feature that is not available through the common interface. Otherwhise it's tightly coupled - game over. If you have different kind of bots with different features (e.g. CleaningBot vs SurgicalBot) you'd have to consider specializations of the CoreBot interface, and let the viewer use the specialized interfaces instead of the implementation directly.

Keeping bots and viewer connected but decoupled

Then, how to connect the viewer with the bots. The important thing here is to foresee some kind of registration mechanism that is exposed via an interface. For example:

  • you could inject the dependencies, for example at construction
  • the viewer could register to the bot as an observer. So the viewer is hidden behind an interface, and the bot dialogues with the viewer with notifications.
  • the bot could register to the viewer, so that the viewer can use its services.

How such registration is performed is left open. Your diagrams are too ambiguous (maybe a C4 model or even some UML could help to give more precise information of what's behind each shape): It's not clear to me if it is a run time issue with independent bots and viewers working on independent devices or a compile-time configuration within a single software system.

But regardless if you solve it by requiring sole programmatic wiring during the initialization of your system, if it is driven by the user interface, or if it uses a mediator or some kind of more complex discovery services, the some kind of injection or registration mechanism will be needed.

Edit: A possible solution

Following the exchange in the comments it appears that bots and viewers are all in the same software solution. The decoupling issue can then be narrowed down to decoupling the library from any concrete bot viewer implementation. A potential approach could be:

UML class diagram with interrelated BotObserver and CoreBot interfaces, the viewer being an implementation of the observer and the bots of corebot

Organizing the viewer registration to the bots when objects are created offers the greatest flexibility: you could well have different viewers for different sets of bots.

I would not recommend it, but if you want to automate the registration at bot instantiation, you could define a kind of static property somewhere, that would keep track of the default observer(s) that has need to be automatically registered at every CoreBot instantiation. It's a variant of a a service locator with all its drawbacks, but it could suit the needs.

  • Thanks for you response. The viewer do not need to know anything about the implementation of the bots, as the only send out messages and the viewer then need to pick them up. I am leaning a bit towards the observer patern, but how can the bot itself register to the viewer? We currently don't support bots being added during runtime, since the viewer need currently need to know how to handle messages for specific handler => which in all cases is in the same manner. Commented May 7, 2021 at 10:56
  • @IamnotFat if only interface is used, it’s already a very good start for decoupling the rest. But to answer your registering question, I’d need to know if bots and viewer are independent software systems (and how they communicate) or if bots and viewers are one and the same software (i.e. in the end it’s only about interaction of objects, regardless if it’s a software bot or a proxy for a remote bot).
    – Christophe
    Commented May 7, 2021 at 11:44
  • The individual bot x y and z is not seperate applicantion itself, but is a library implements the interface... Commented May 8, 2021 at 7:12
  • @IamnotFat ok. So all these are components of the same software; no interprocess communication; no network communication/messaging. This confirms the observer as an interesting candidate. I understand also that there is no runtime addition of bots, so the library classes will be instantiated and "wired" with the viewer at some point, for example at initialisation. I've updated my answer accordingly.
    – Christophe
    Commented May 8, 2021 at 9:05
  • 1
    @IamnotFat I think what you are looking for is the scenario of the last paragraph: you register your viewer that implements the observer interface to the library, e.g. setDefaultObserver(BotObserver). The bot implementation would the register automatically the default observer, e.g. subscribe(getDefaultObserver()).
    – Christophe
    Commented May 10, 2021 at 14:11

There are two common ways to handle this.

A) Through Bot.Core itself

The main issue here is that the Overviewer has no access to the Bot X type, so it cannot reference it for config purposes. But Bot.Core can reference it.

If you add a ConfigureBotX() method to your Bot.Core project, that method can do the configuration of the bot, and the Overviewer can just call that method instead of doing the config itself.

  • Pro Overviewer only needs one dependency to work.
  • Pro Where relevant, Overviewer doesn't need to know the nitty gritty details of configuring Bot X. This makes it so that changes to how Bot X is configured could be contains within Bot.Core, without affecting the Overviewer (barring a package update)
  • Pro This approach shields Overviewer from knowing or being able to influence which bots are being used. If that is a concern for you.
  • Pro or con? When Bot.Core is upgraded and makes breaking changes, this approach forces you to make all bot libraries compatible with the new version before you can release it.
  • Con The Overviewer is not able to use some custom configuration that Bot.Core hasn't already prepared for
  • Con You have to add a method for every Bot X/Y/Z/... dependency, and when developing a new Bot A, you have to update Bot.Core to account for it.

B) Let Overviewer directly depend on Bot X/Y/Z/...

This is a very common approach for a library that has multiple compatible provides, such as Entity Framework and its many provider packages.

Now, the Overviewer is able to do the configuration itself. Bot.Core now has no direct knowledge of the Bot X/Y/Z/... libraries. Instead, the Bot X/Y/Z/... libraries implement the correct interface so that Bot.Core is able to reusably handle all of them.

  • Pro Overviewer only has to load the Bot X/Y/Z... packages that it actually wants. If it doesn't want X, then it doesn't need to be loaded
    • Related con This approach makes Overviewer able to know and control which bots are being used.
  • Pro This makes it possible for anyone to make a bot library and have Bot.Core handle it. This may be very desirable if there is interest in a large variety of bots.
    • Possible con This may not be desirable if you wish to strictly control which bots libraries can be used, e.g. for licensing purposes.
  • Pro You can develop new bot libraries without needing to update Bot.Core itself, as long as the new library implements the current Bot.Core contract.
  • Pro Overviewer is able to perform some custom logic without needing Bot.Core to provide the configuration possibilities.
  • Con If each bot has very unique abilities, you may not be able to create a reusable interface that Bot.Core can account for. This means either having to create bot-specific interfaces in the Bot.Core (which defeats the purpose of reusability), or offload the bot-specific logic to Overviewer and ensure that Bot.Core only handles the reusable parts.
  • Pro or con? When Bot.Core is upgraded and makes breaking changes, this approach allows you to release the new Bot.Core before the related bot libraries have all been made compatible.
  • Bot.Core don't know about the different bot implementation, and neither does the Bot overviewer. But having the different bot implementation somehow could register itself on the bot overviewer could be need? I cannot see how that should be possible though, since they are not connected? Thanks for the response Commented May 7, 2021 at 12:40
  • @IamnotFat The point is to then connect them if that approach suits your scenario better.
    – Flater
    Commented May 7, 2021 at 12:43
  • The bots are client specific - and not part of the general core component.. Some might need xyz, others only zy.. so connecting it back to the overviewer would make it more coupled Commented May 7, 2021 at 12:58
  • @IamnotFat "coupled" does not mean "referencing more than one package/project". In the second approach you decouple the bot libraries from the bot core, even though this means the overviewer has more than one package/project reference.
    – Flater
    Commented May 7, 2021 at 13:03
  • @IamnotFat: Maybe a better way to explain it: "decoupling" is about how many separate boxes you have in your diagram, not how many lines there are connecting the boxes (this is only sometimes the case, but not always, like in my suggested second approach). In the second image, you've disconnected (= decoupled) bot.core from the specific bot x/y/z/ libraries, which is therefore more decoupled than in the first image.
    – Flater
    Commented May 7, 2021 at 13:40

Where each bot implementation is separated into its own application. This causes the issue of how do I then register the different implementations of the bots?

Use Plugins and do it at runtime.

You could either add it to Bot Overviewer or Bot.Core but that might feel a little bit clunky. Those two modules are very stable modules and might rarely be touched after initial development, whereas bots might change every day. You don't want to rebuild either just to accommodate a new bot. Especially if you are planning to have 100 different bots.

A way to solve it is to bundle each Bot independently together with enough meta information to fully describe it and load them at runtime. In Java for example a Bot might implement a specific interface defined by Bot.core and then be bundled as a jar-file. Bot overviewer can then have different ways to find jar-files containing plugins:

  • All Plugins are stored in a directory and loaded at startup
  • There is a UI control to interactively load a plugin through a file open menu
  • There is a configuration file that contains a list of plugins

You might want to check out Robocode, a game in which players write a Bots in Java, bundle them as Jar and then let the game load them at runtime and fight each other: https://robowiki.net/wiki/Main_Page


I'd throw a queue or two in there.

  1. Bots put their data onto a queue
  2. Bot Core reads the queue and does whatever it does.
  3. Bot Core publishes interesting things to another queue/topic
  4. Bot Overviewer reads the queue/subscribes to the topic.

Payload contains any required details of the bot's implementation, such as name.

You can do this through either actual queues or structures/classes that implement various interfaces (IBot, IBotCoreStuff etc.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.