Clean Architecture - Robert Martin - How to connect use cases
Asked Answered
O

2

19

I'm trying to implement the Clean Architecture described by Robert Martin. More specifically I'm using VIPER which is an iOS version of Clean Architecture.

The problem I have is as follows:

The user starts looking at a map with places (pins) on it. If he clicks a button, a pin is dropped and he is taken to another view to create (or edit if it was a click to an existent pin) the place (or cancel). In this other view, the user can edit the place's information and then click "back" or "done" (or "edit"). If he clicks "done", the PlaceDetailsViewController sends a message to the PlaceDetailsPresenter with the place information and the PlaceDetailsPresenter uses the CreatePlaceInteractor to create the place. This interactor returns the GUID which is used to identify the place.

If the user clicks back before creating the place, he gets back to the map and the dropped pin goes up and away (since it has no GUID, it is a new place and goes away). If he clicks back after creating, the pin stays there (because it should have a GUID).

How should I connect all that and where should the place information (including GUID) be stored? To clarify a little bit more:

  1. Who should inform the MapPresenter that the pin stays there or goes away? Is it the PlaceDetailsPresenter or should I pass this information to the PlaceDetailsWireframe -> MapWireframe -> MapPresenter -> MapView ?
  2. Before going back, where should this GUID be stored, in the PlaceDetailsPresenter or in the PlaceDetailsViewController?

Right now that's what I have: enter image description here

EDIT:

Basically I think the problem is that VIPER came from Robert Martin's Clean Architecture and he comes from a Web (Rails) background, so he doesn't think much about state (or don't specify it in his talks).

Which is mainly my question, where should the state be stored, how should the different modules communicate, should it be through the Wireframe, or through the database, or through the interactors, or through the Presenters communicating with each other like here https://github.com/objcio/issue-13-viper-swift.

Odyssey answered 6/3, 2015 at 5:25 Comment(3)
I'm just starting out with VIPER but it feels wrong to me to have presenters know about each other. I favour modules communicating with each other through the Wireframe/Router. I'm happy to be corrected here as I'm still learning this architecture.Midriff
I tend to think the same way you do, though I still haven't found a decisive answer for how to pass information between them, whether it's better to pass data structures through wireframes (2 options here, use always the same, big, data structure, or keep changing along the way to pass just the minimum amount of information necessary (which ends up creating more non-reusable classes)), or save state in Interactors and use them to retrieve the information in the other presenter. I posted another stackoverflow question that explains what I mean by big data structure, just couldn't find it yet.Odyssey
#29055026Odyssey
N
30

I don't know much about Viper, so I can't comment about that. However, the gross state of the system should be held in the entity objects and manipulated by the interactors. The detailed state of the GUI (selection rectangles, etc) should be managed by a special connection between the controller and the presenter.

In your case there are two screens. The map, and the place editor. Clicking on the map causes either the placePinController to be invoked. It gathers the location of the click, and any other contextual data, constructs a placePinRequest data structure and passes it to the PlacePinInteractor which checks the location of the pin, validates it if necessary, create a Place entity to record the pin, constructs a EditPlaceReponse object and passes it to the EditPlacePresenter which brings up the place editor screen.

If the Done button is clicked on the place editor screen it invokes the EditPlaceController which gathers up the edited data into an EditPlaceRequest data structure and passes it to the EditPlaceInteractor. etc..

You specifically asked about the GUID of the pin. That would be created by the Place entity and passed back to the editPlacePresenter PlacePinInteractor.

Nidia answered 8/3, 2015 at 16:56 Comment(6)
Maybe I wasn't very clear, but what I ment was: the click happens in the MapPresenter, but the place only gets create later, if the use presses "done" in the PlaceDetailsViewController (which is like the view). When the user clicks "done" the place gets created. If the user clicks "back", then it should go back to the MapPresenter and then not dismiss the pin (or dismiss it if it wasn't created).Odyssey
By state I mean things like the pin that was temporarily created in the Map. So that when we come back from the PlaceDetailsViewController/Presenter, we know which pin to dismiss or maintain (and give the GUID to associate with).Odyssey
Think of "done" and "back" as different use cases (different interactors).Nidia
what would the "back" interactor do?Odyssey
@RobertMartin assumes that clicking on the map creates an Entity. You assume that you should only create an Entity if the creation process is finished. Both solutions will work but require different channels of communication. Bob's way is easier if you can create the Entity with missing values w/o violating business rules: if the back button is clicked to cancel the creation, delete the Entity. If that doesn't work, use temporary DTOs to pass around. (NSDictionary might do, but I recommend custom Value Objects for data sanitization.)Hamburg
@RobertMartin : "...constructs a EditPlaceReponse object and passes it to the EditPlacePresenter which brings up the place editor screen" - How will the EditPlacePresenter know where to display the place editor screen? That information (top view, or something like that) is in the MapController.Odyssey
F
0

In pure VIPER Router should hold modules input in form of a protocol. And modules Presenter should conform to it. So when Router uses other modules Router to assemble new module it passes it's input to it.

The second Router then assigns the input to its Presenters output. So basically Presenter of the first module becomes a delegate for the second modules Presenter.

So in your case when a user selects a place MapPresenter asks MapInteractor for a GUID and tells MapRouter to navigate to details for this GUID.

MapRouter asks PlaceDetailsRouter to assemble PlaceDetailsModule for this GUID and passes MapModuleInput to it. PlaceDetailsRouter assigns MapModuleInput to PlaceDetailsPresenter. PlaceDetailsRouter puts GUID in the PlaceDetailsInteractor

Flagellate answered 8/3, 2018 at 17:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.