Quantcast
Channel: User Filip Milovanović - Software Engineering Stack Exchange
Viewing all articles
Browse latest Browse all 163

Answer by Filip Milovanović for Can the presenter talk to to the controller?

$
0
0

So, there are a couple of things to understand about this image - the primary purpose of this diagram is to depict how to organize dependencies between components that are in different layers. So, the central focus here is on the Interactor; the important bit is that, besides the Interactor, there are some ancillary types + data structures that form the input boundary (it's like the Interactor saying "this is how you request my services"1), and the output boundary (the Interactor saying "here's what to submit if you want me to call you"2).


1 That is, the Interactor provides an interface (a set of public methods and properties), including their parameter and return types, that external code can call. It's the public API of the Interactor.

2 What I mean by "submit" is: you give an output object to the Interactor at construction time (dependency injection). The Interactor accepts as a dependency anything that implements the output boundary. The Interactor has to define what its output boundary is going to look like, and then clients calling code conforms to that. E.g. it could be an interface (say IPresenter) where you'd inject a presenter into the interactor, or in simple cases, it could just be a signature for a callback method, where you'd inject a callback.
The code that constructs the Interactor will also pick the presenter/callback so that the Interactor can push data to it.


enter image description here

The fact that there's a Controller and a Presenter in this diagram is less important - these just represent some UI-related (or presentation side–related) pattern that makes use of the input boundary, and plugs into the Interactor via the output boundary. The way the boundary is crossed is the important bit. As for the exact presentation-side pattern, you could in principle have some other arrangement, this doesn't go against the ideas/principles of Clean Architecture.

"but how the controller is accessed in the case of the web?"

What Robert Martin is depicting here is some variant of the Model-View-Controller or Model-View-Presenter UI pattern, which is something you'll find in desktop applications, or JavaScript-based client-side framework such as Angular, or, arguably, React. As depicted, it doesn't necessarily map 1-to-1 to server-side web controllers, which handle requests from a remote client.But read on, you'll see that these differences don't really matter.

Another thing to understand, which I already hinted at, is that the boxes in the diagram represent somewhat abstractly the elements or components of the architecture - i.e., the roles these code artifacts play within the architecture. A box isn't necessarily an object/class: most boxes here can be implemented in different ways depending on the language you use, complexity of the project, or other factors like the frameworks you use, or personal preference. So, for example, the Presenter could be an object, but it could also just be a function (a callback, a lambda), or even an object composed of a couple of other objects (which you can think of as being a small encapsulated bundle of objects that work together).

Finally, the input boundary and the output boundary are different from the perspective of the Interactor, but ultimately, it's code that calls or, more typically, the code that creates the Interactor, that decides what implementations to pass in. This is the flexibility that's afforded by the CA decoupling. The controller and the presenter can in principle be one and the same object. Or, the presenter could be separate object that the controller holds on to internally (it could be a member field of the controller), and knows how to interact with. The "presenter" could just be a method on the controller itself, that's passed in as a callback to the Interactor; such a method would thus have access to controller's private data. Or the two could actually be separate objects. Or you could do it differently in different places of your application (this may be warranted at times, but I'd try to stay consistent).

The output boundary is typically supplied to the Interactor's constructor (constructor injection), but you can also have a variant where you supply the output boundary (the presenter) as a parameter to the method you call on the Interactor - this lets you decide what should handle the output at each call site. In the simplest case, your "presenter" could just be a lambda created in-place, that updates something when it gets called by the Interactor.

See my answer here for more details.

So, it all depends on your needs, on how you compose the components of the application together. But for the most part, try to keep things as simple as possible.

In the same vein - as in basically any diagram - not every detail of an actual application is captured in the diagram. You'll have to fill in some of the blanks. For example, the "Entities" box is certainly not one object, but is more of a stand in for a class diagram of the domain entities that the Interactor manipulates. So don't take the diagram too literally.

"one final question, say suppose after invoking the use case, u, which was successful but the UI wants some more data to display the result to the UI.

so where does the logic of fetching more data reside. ?"

While this exact scenario is not depicted in the diagram, something very similar is. Fetching more data is, in other words, calling some remote service. A database is a remote service that has a certain API. There's a "Data Access Interface" associated with the Interactor (Interactor saying "I know when to fetch data and what to do with it, but you have to tell me where from") - so you implement an object that conforms to this interface required by the Interactor, and you inject it.

enter image description here

You'd do the same thing for the remote service - the Interactor needs to specify how it wants to interact with the remote service by defining some sort of an interface (we could call it "Service Access Interface"), and then you have to implement it in some outer layer, and inject it.

If the decision to do the fetch can be made within the flow of the use case, then there's a use-case level (business) rule that determines when and under what circumstances the fetch should be initiated. This rule would be the responsibility of the Interactor (the rule would be, well - encoded - by the code of the Interactor), and the actual fetching mechanism would be the responsibility of whatever component implements the "Service Access Interface").

If the decision cannot be made during the execution of the use case - if, for example, it's the user who makes this decision, by performing some action - then the fetch would be the result of a separate round-trip. The Interactor performs the initial use case; after that, its job is done for the time being. The user then does something in the UI, the controller calls some method on the Interactor, and the Interactor performs the fetch, eventually pushing the result through the output boundary (side note: the "eventually" part can be quite literal when it comes to async calls).

Or, if the Interactor itself can determine that there's going to be a fetch at a later time, but cannot know when (again, maybe this depends on user actions), the Interactor can return to the caller an object that can perform the fetch on demand ("here, when you're ready to fetch the data, call a method on this thing"). Or some such scheme.

So you have to think about the usage patterns, business rules, etc., and design your interfaces to support that. The CA gives you an overall structure, a way to think and talk about the architecture in broad terms, but doesn't impose on you the details of the actual design - it's not prescriptive when it comes to that, because these details depend on the intricacies of your business domain.


Viewing all articles
Browse latest Browse all 163

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>