Pergunta

Use Case Interactor:

Use Case Interactor

We can see it in this picture how does a request cross the layers until it reaches the Use Case Interactor.

How does the controller pass the request towards the Use Case Interactor? By the request i mean the data from the user ( maybe a form )

I understand that the Input Port is an interface, but how do you pass data across it towards Use Case Interactor?

Also, does every Use Case has it's own Input Port? If so, that doesn't make much sense, why not just use the Use Case as a reference in controller instead of adding a layer of abstraction with an interface. I mean what is the point? The layer in which controller resides knows about the layer where Use Case is located. What i mean by this is the following:
If i have a class: AddUserUseCase, does that class need to have it's own input port (interface): AddUserInputPort. Or can we make something more generic, like UserInputPort and then have all UserUseCases implement that?

Foi útil?

Solução

This picture shows a generalized view of various elements that form the components and the interfaces between the layers. When I say interfaces, I don't mean the interface types (like in C# and Java, represented in UML by the <<interface>> stereotype), but I use the term in a more general way - to denote the set of public (externally visible) elements exposed by a software component (so, public methods and properties in the case of a class, or the set of "surface level" classes of a component or a layer, etc.). The diagram is generalized in the sense that you don't have to have all the elements on it as classes in your program. You'll see what I mean in a moment.

How does the controller pass the request towards the Use Case Interactor?

So, the interface (in the sense described above) of the Use Case Interactor consists of two things - the Input Port interface type and the Request Model data structure. The Request Model is the input to the Interactor that goes through the input port; this doesn't have to be a class or a data structure, it could just be the list of parameters to a method defined on the input port - but is shown here in a more generalized way as its own thing. The Controller interprets the request (from the view), figures out what it means in terms of the business logic and the supported use cases, and then calls the appropriate method on the appropriate use case, with the appropriate parameters (so it either constructs a parameter list or an instance of the Request Model structure) - this is how the "request" (reinterpreted in the context of a given use case) is passed on.

Now, note that the Request Model is conceptually owned and defined by the use case, it's part of its interface (the so called provided interface), and that it has to be on that side of the architectural boundary, in order for the use case layer to remain independent of the upper layers (the rule about the direction of the dependencies). The contract between the layers there is that the Use Case is sort of saying "here, I provide these methods and these data structures for them to use as input if you need to use the services I offer".

Similarly, the Output Port interface type and the Response Model data structure form together an interface for an extension point (a so called required interface). I.e., this is a place where you can take some behavior and plug it into the use case. This is what the Presenter does by implementing the Output Port; this allows the use case to call the Presenter without ever knowing that it even exists. This is classic dependency inversion, and again, it controls the direction of the coupling across the architectural boundary. Again, the Response Model could be a separate data structure or simply a list of parameters. The contract defined by the use case is "you can plug in here by implementing this interface, and I'll call you and provide data using these data structures".

Now, one point of possible confusion is that in a real application, you won't usually have things named like "OutputPort" or "UseCaseInteractor". These are just generic names used to talk about these concepts. A Use Case Interactor will have some meaningful name, e.g. "Reservations" (well, meaningful in the context of the domain that's being modeled); the Output Port could be an interface called "IReservationsObserver", and anything that wanted to observe interesting changes would implement that interface and register itself with the Reservations object (this is just one possibility, don't take that as a rule - it's an example off the top of my head, based on the Observer Pattern). The Input Port could be an interface called "IReservations" that would provide reservations-related methods; maybe something like "MakeReservation(ReservationRequestDetails details)", where "ReservationRequestDetails" is the Request Model (a data model for the specific use case scenario).

The (green, presentation layer) Request is a part of the Controller's interface (it's conceptually owned by the Controller)2. Often, the data it contains will not be in a suitable format for the use case layer (say, numbers could be represented as strings, or it may contain presentation-specific fields that make no sense in other layers); the Controller takes that, and then uses it as a basis to construct a (red, use case layer) Request Model that more directly expresses the input to the use case.

Also, just to be clear, there generally isn't just one Use Case Interactor that handles all the use cases. Also, as you've seen by now, you shouldn't take the diagram too literally; the Use Case Interactor box could represent one class, but it could also be a stand-in for a small group of collaborating classes. In the same vain, the Request and Response models could actually represent a group of types that serve as parameters to various methods on the external boundary of the use case.

Also, does every Use Case has it's own Input Port? If so, that doesn't make much sense, why not just use the Use Case as a reference in controller instead of adding a layer of abstraction with an interface. I mean what is the point?

You don't have to do that; what you want to do is to control the direction and the number of dependencies between the layers. The outer layer depends on anything that it directly references from the inner layer (any of the types or any other elements1 from the inner layer that it uses in any way - as a service, for internal logic, parameters, etc.). These referenced types/elements form the "outer surface" - the exposed public interface of the inner layer. These have to be relatively stable against changes; if you make changes to those, they will propagate to the outer layer, because it directly depends on them. Behind this "outer surface" the inner layer is effectively encapsulated, so you can restructure and refactor it.

Now, in the image you've provided, the dependency structure is such that the presentation layer does not have any knowledge of the concrete Use Case Interactor, which means that it's internal to the Use Cases layer, and that you can fiddle with it more or less without affecting the outer components. But you don't have to have that exact dependency structure. The Input Port doesn't have to be an interface type, it could be a concrete or an abstract base class, or you may opt to not have it at all. In the latter case, the Controller would directly depend on the Interactor, which would make the Interactor part of the "surface level" types. Then if you wanted to change or reorganize the internal implementation, you would make sure to not make changes to the public elements of the Interactor, and, if the changes are more involved, you would maybe extract a subset of its code into a new class during refactoring - which would then lead to the structure similar to the one on the diagram - except what was your Interactor would now assume the role of, say, the Input Port.

Or can we make something more generic, like UserInputPort and then have all UserUseCases implement that?

I hope you see that use cases are objects designed to do specific things (support specific usage scenarios), so in general there's no much point in making a generic interface for a group of them (that's not the idea), although it may make sense to do so in specific cases.


1 "other elements" could be anything that's part of the "contract" between the two layers - an expectation that certain data will have a particular structure (even though it isn't explicitly defined as a type), a convention of some sort, etc.

2 The View/ViewModel/Controller part of this diagram is a little ambiguous; the exact dependency structure there depends on the MVC/MVP variant used. The View may have a direct reference to the Controller, for example (in order to call it and pass the Request). Or not - maybe the Controller polls or subscribes to an event queue that's not shown. The interaction between the View and the ViewModel may be supported by the data-binding mechanism of the framework used. The Presenter could be merged with the ViewModel, or even with the Controller, etc.

Outras dicas

Every architectural framework has its own set of building blocks, which can be useful starting points but are by no means set in stone. Uncle Bob even mentions this in his article on Clean Architecture:

Though these architectures all vary somewhat in their details, they are very similar. They all have the same objective, which is the separation of concerns. ... There's no rule that says you must always have just these four [circles]. However, The Dependency Rule always applies. Source code dependencies always point inwards.

The ports and interactors essentially boil down to having a domain model that is decoupled from the UI layer (i.e. the Dependency Rule). Ports are interfaces in the sense that they define a contract between the layers, but you don't necessarily have to use an interface in your code. Keep it simple and do what makes sense for your application.

Licenciado em: CC-BY-SA com atribuição
scroll top