C++ – “Crossing Boundaries” in Clean Architecture

Architecturecdependency-managementdesign

I am a layman without any programming-like education but I spent the better part of my free time to get into programming bots for some games in first AutoIt and then C++. I was introduced to programming in a very procedural style only using free functions. A couple of weeks ago, my focus shifted from actually making these bots to understanding how professional programmers are structuring their programs and inevitably I came across OOP, Design Patterns, SOLID and most recently Clean Architecture.

I want to say beforehand that I know full-well that these techniques are most likely counter-productive for the tiny programs I write; however, I want to get to know them out of the interest of learning.

I am currently trying to split the program into smaller parts to stick to the SRP and I’m sort of trying to implement the idea(s) behind the Clean Architecture. Since it’s an incredibly smaller program than what Clean is targeted at and since I’m most likely the only one who will be working on it, I am not too strict with the separation into Controllers, Presenters, ViewModels, Views and so on – also I’m not planning to separate the program into different DLLs in order to have the GUI in a completely different place. However, I am fond of the idea to have GUI absolutely dependent on the (business-) logic whilst the logic doesn’t know about the GUI at all, thus I will treat the GUI-Module as if it was in a different project. However, the “crossing of the boundaries” is something I cannot, for the life of me, understand how to implement, especially if it was from an external DLL to the application.

Correct me if I’m wrong: There is a Controller which sends a request to the Interactor-Object which will deal with several Entities and eventually return a result to the Presenter which takes care of the changes in the GUI. Basically, my input calls a function that returns some value after working with the app. This function-call is put into the Interactor-Object in the style of the Command-Pattern. It implements some Boundary-Interface* which is made public. That’s necessary to build the Controller in the first place since it needs to know the signatures it can call. Therefore, that interface becomes the access-point for the external DLL to send requests to the application.

However, wouldn’t the Interactor need to get references to the Business-Entities and the Presenter/s on construction so it can interact with them? The Controller cannot pass them into the Interactor as it doesn’t know anything about the Entities, which means the Controller would have to call an already built concretion of the Interactor – how is that being done? In my mind, the Application would have to pass the concrete Interactor to the Controller which is forbidden as the “inner circles” are not supposed to know about the outer ones.

This brings me to the second part, crossing the boundary outwards: How does the Interactor pass the response to an outer circle without knowing about it? I could imagine this is some sort of Observer-Pattern as the application does provide the interface the Presenter/s will have to implement, so the interactor does know the signature of the Presenters. However, this would require the Presenters to register themselves to a concrete Interactor-object which ends up in the same question as above.

Thank you in advance – any input would be appreciated.

TL;DR: Where do you instantiate UseCases/Interactors and how do you access these concretions from outside?

Best Answer

First, let me strongly recommend that you read Robert Martin's book titled Clean Architecture if you haven't. I assume that you have read it, but I don't know for sure, because many questions that are very similar to yours were asked before he even published said book.

If you have read his book, then go back and take a look at Chapter 26: 'The Main Component' again.

If you don't own the book (and for anybody else who doesn't), let me summarize the given chapter for you.

The first few paragraphs of the chapter say:

In every system, there is at least one component that creates, coordinates, and oversees the others. I call this component main.

The Main component is the ultimate detail--the lowest-level policy. It is the initial entry point of the system. Nothing, other than the operating system, depends on it. Its job is to create all the Factories, Strategies, and other global facilities, and then hand control over to the high-level abstract portions of the system.

And the 'Conclusion' section of that chapter says:

Think of Main as a plugin to the application--a plugin that sets up the initial conditions and configurations, gathers all the outside resources, and then hands control over to the high-level policy of the application.

Let me specify something really quick. I will mention (and already have mentioned) the word 'component', but technically a component is not a thing in the sense of code. Put simply (maybe too simply), a 'component' is just the word that Uncle Bob uses to refer to what is essentially a group of related code/code files (refer to his book if you want his actual definition).

Therefore, when I speak about a 'component', remember that I am actually talking about the code within that 'component'.

How then does one component depend on another then? Well, according to Uncle Bob, a component (a grouping of code) dependency is created any time you reference anything in a different component; whether that be an interface, a type definition, or even a simple variable name.

Keep this in mind, because technically a component cannot be injected into another. When I speak of injecting one component into another, I am almost always talking about dependency injection specifically on the level of the interfaces/classes that are contained in those components.

Now, back to the quotes: those mean that the Main component, which is the entry point of your program (such as the 'main' file in C++, or the first file to run in your NodeJS server, for example), should either instantiate the concrete classes and give ('inject' is the technical term for it) them to the dependents, or should give the dependents a Factory (which is a design pattern for those of you who don't know) whereby they can instantiate the concrete classes themselves.

In Chapter 14: 'Component Coupling' of Robert Martin's book, he provides an image that I think illustrates the topic of Chapter 26 very nicely:

enter image description here

Note: This is a Component dependency graph.

Notice how many components 'Main' depends on but also notice how absolutely no components depend on 'Main'. This is because the 'Main' component does all the wiring together of all the other components, so that they don't have to know about each others' concrete class implementations but can instead depend only on the interfaces/abstractions.

Now that you (hopefully) have a basic understanding of what I'm trying to say, let's discuss the order in which dependency injection happens.

Note: It is important to be reminded that I am talking exclusively in the context of interfaces/classes after this point.

The order in which you create and inject the dependencies into each other depends (pun not intended) on two things:

  1. The dependency injection mechanism;

If you inject the dependencies through the constructor, then obviously the dependencies must be created before the dependent and injected into the dependent during its creation.

However, if you can inject the dependencies through a method on the dependent, then obviously the dependency can be created after the dependent and simply injected then.

If you are using an injection mechanism that can be done at any point (such as through a method), then the next point doesn't really apply.

  1. The kinds of the dependencies.

Are the dependencies using dependencies, or are they implementing/inheriting dependencies?

For example, imagine that a concrete class inside of Component B uses an interface from Component A.

enter image description here

This means that the concrete class is expecting the interface to be implemented elsewhere by a different class. It is only using the interface, not inheriting from it.

Now imagine that Component C has a concrete class in it that uses an interface that is in the same component as itself. This concrete class does not implement said interface, it only uses it:

enter image description here

The implementation for the interface actually resides in Component D :

enter image description here

Let me phrase this again: Within Component C, a concrete class uses an interface, but that interface is actually implemented in Component D.

The 'uses' and 'implements' relationships are both equally dependencies, but this is the important part: when it comes to the order in which dependencies should be injected, only the 'uses' relationships matter.

Let me illustrate that statement with an example:

First, let's imagine that you have a dependency graph that looks like this: enter image description here

As I said earlier, the only relationships we're looking for in this context are the uses relationships, so consider only the black arrows.

Component #4 depends on Component #3; Component #4 depends on Component #2 which depends on Component #1;

Let's imagine that we're injecting all of the dependencies through constructors.

In pseudo-code, the Main component would look something like:

// Instantiate the concrete implementation within Component #1:
implementation1 = new Implementation1() 

// Instantiate the concrete implementation within Component #2 while injecting its dependency into it:
implementation2 = new Implementation2(implementation1) 

// Instantiate the concrete implementation within Component #3:
implementation3 = new Implementation3() 

// Instantiate the concrete class within Component #4 while injecting both of its dependencies into it:
 implementation4 = new Implementation4(implementation2, implementation3)

// Okay, everything has been wired together, now hand control off to your highest level application logic.

It's important to note that Implementation3 could have easily been instantiated before Implementation1 and Implementation2, but I decided to instantiate them in numerical order, because it is easier to think about that way.