"If the call for the financial report comes from the web view then the
parameters received by the Financial Report Controller (for example
date) are probably in JSON format. If it comes from a print
instruction on a CLI application then it could be plain text."
You don't pass in either of these to the interactor.
Note the Financial Report Request
element; it has a <DS>
tag on it; that's short for a data structure. It's either an actual data structure, or a just parameter list that a method on the Financial Report Generator
has. The choice there is yours; creating an actual data structure creates better separation, is more explicit and more flexible, but it's also more work.
So, what you pass in is the data in the format specified by the request model. You don't pass in either of the source formats to the interactor, as these are external formats (unless you have purposefully adopted one of those formats as your request model, and accepted the tradeoffs that come with that). The controller module will convert the data to a Financial Report Request
(it will either initialize the data structure, or if it's just parameters, it will just extract the values from the source data, and pas them as parameters), and then it will call a method on the interactor (Financial Report Generator
) with that data.
"My second question is about the presenters. There is a Screen
Presenter and a Print Presenter. If the controller is called to get a
financial report, how does it know at the end which presenter to use
?"
This is an inheritance diagram; this is not depicting an instance of a Screen Presenter
and an instance of a Print Presenter
floating around. In the setup depicted, at runtime, there's just one or the other. The controller doesn't know which kind of presenter it's connected to, and it shouldn't - that's the whole point of dependency inversion. It only has a member variable that has the type of the Financial Report Presenter
interface.
Some other code that knows about both (what's called the composition root, e.g., main, or a dependency injection container) will pick a concrete presenter and inject that instance in the controller. Then later on, when the controller has to use it, it will take the Financial Report Response
returned by the interactor, and will use it to call some method (or maybe several methods) provided by the Financial Report Presenter
interface; this will polymorphically invoke a method on the concrete presenter that has been "plugged in".
Depending on how your application is structured, if the business logic of the interactor (report generator) doesn't require the same instance being shared, in your composition root, you might simply create one instance preconfigured to use the screen presenter to be used for screen-based tasks, and another instance preconfigured with a print presenter to be used for printing.
Or you might use the Composite pattern to combine several presenters into one (derive a Composite Presenter
from the Financial Report Presenter
interface, wrap several existing presenters). Your composite presenter could have some logic that allows it to switch to which of the child presenters it forwards to. Or, if you wanted to have the ability to output to all presenters simultaneously, you'd indiscriminately forward all the calls to all of the child presenters.
Or, a method on your controller could accept a derivative of the Financial Report Presenter
interface as a parameter, so every time you call that method, you can pass in a different presenter.
The setup depicted is pretty flexible, but no design works well for every imaginable set of constraints, so depending on the details of the actual application and on it's change patterns, you might adjust some elements of the design.
for example the company now wants to get financial reports in audio format so you can listen to it if you are blind or can't use a screen, how would you do it ? And what about controllers if the input is now in voice format too ?
Well, I understand this is hypothetical, but, using voice recognition for input brings it's own set of problems beyond just having to deal with another format. However, suppose you already have a module (or even another application) that handles those problems for you - that module (or application) would ultimately output commands in a form of some data structure, that would then be converted to the Financial Report Request
, which would then be passed to the report generator. On the other side of things, you'd have an Audio Presenter
and an Audio View
that would probably include a voice generator, that would accept it's own input (as plain text or some other format). This data would be generated based on the output of the interactor (the Financial Report Response
data structure).
"If I have 10 types of reports, does that mean that the Screen
Presenter and Print Presenter have to implement 10 interfaces?"
I wouldn't do that, because that will likely lead to a big tangled mess of a class. Don't forget the single responsibility principle (SRP). (BTW, let go of the idea that you "have to" do things in a certain way - you (and your team) are the one(s) making design decisions, you can do whatever makes sense for your specific set of problems, nobody is going to grade you on this. Think of Clean Architecture as of a map, not a prescription.)
How you'd go about this completely depends on the details of your application & business logic. E.g. if some of your reports use essentially the same data, but just show it in different ways (either visually, or maybe aggregate it along different axes, showing different types of summaries), you might opt to have a presenter for each kind of report, and have them all implement the same presenter interface. It's this interface that defines the input for the presenters, but note that, in this design, it's owned by the controller (the controller says "I provide these kinds of outputs, and these are the output interfaces I support; if you want an output from me, implement the corresponding interface"). So you might also choose to have a couple of controllers (e.g., if the way users interact with the system is sufficiently different in different scenarios). Or you might have methods on your controller that accept different kinds of top-level presenter interfaces as a parameter. Or a couple of controllers with such methods. Again, depends on the actual application, and on how far you want to go in adhering to separation of concerns.
You might be thinking that's too much work, but remember, these classes are supposed to be fairly small for the most part, and focused on a fairly narrow task. Another point, Clean Architecture is about structuring the dependencies; it doesn't really matter if all of its elements are represented by classes/objects - in principle, you can do it all with functions. Also, you don't have to get it all right at the very beginning, start with a best guess then evolve over time (that's why I said it's a map). See my answer here to see how you might arrive at a clean architecture–like dependency structure, starting from a typical function with an input and an output.
For reports that are fundamentally different, you might introduce a different presenter interface, and a different set of presenters. You'd also have to consider (or check by experimentation) how feasible your approach is - maybe one of the reports that's based on the same data takes too long to generate, but the others work fine, so you'd treat that one differently (adding a new presenter interface for it). You might also find that some reports involve barely any business logic, so you may decide to bypass the interactor completely and go directly to the database, where you'd rely on an SQL query to transform the data into the shape you need (note that this doesn't break the dependency rule).
Also, you don't have to follow the depicted design to a tee; in particular, if you have functionality that repeats over and over, you can extract/encapsulate it into a new class that you can then reuse. So feel free to introduce new elements into the design, or to split things, etc. (And in fact, there are probably elements that are omitted on the diagram you've posted.)
So, to find the answer, you have to think about what kinds or reports you are going to support, and what's the data you'll be working with, what modules you want to keep independent, etc. And you might not have all the information until the project has lived for some time; if you keep the project evolvable, you'll be able to reshape parts of the dependency structure as the need arises. Also note that not all parts of the codebase change at the same rate; those parts that barely ever change will derive little benefit from a full blown application of design principles; it's enough to keep them decoupled from parts that do change at the boundary where they interact.