سؤال

In his book 'Clean Architecture', Uncle Bob says that the Presenter should put the data that it receives into something he calls the 'View Model'.

enter image description here

Is this the same thing as the 'ViewModel' from the Model-View-ViewModel (MVVM) design pattern or is it a simple Data Transfer Object (DTO)?

If it is not a simple DTO, how does it relate to the View? Does the view get updates from it through an Observer relationship?

My guess is that it is more like the ViewModel from MVVM, because in Chapter 23 of his book, Robert Martin says:

[The Presenter's] job is to accept data from the application and format it for presentation so that the View can simply move it to the screen. For example, if the application wants a date displayed in a field, it will hand the Presenter a Date object. The Presenter will then format that data into an appropriate string and place it in a simple data structure called the View model, where the View can find it.

This implies that the View is somehow connected to the ViewModel, as opposed to simply recieving it as a function argument for example (as would be the case with a DTO).

Another reason why I think this is because if you look at the image, the Presenter uses the View Model, but not the View. Whereas the Presenter uses both the Output Boundary and the Output Data DTO.

If it is neither a DTO nor the ViewModel from MVVM, please elaborate as to what it is.

هل كانت مفيدة؟

المحلول

Is this the same thing as the 'ViewModel' from the Model-View-ViewModel (MVVM) design pattern

Nope.

That would be this:

enter image description here

That has cycles. Uncle Bob has been carefully avoiding cycles.

Instead you have this:

enter image description here

Which certainly doesn't have cycles. But it's leaving you wondering how the view knows about an update. We'll get to that in a moment.

or is it a simple Data Transfer Object (DTO)?

To quote Bob from the previous page:

You can use basic structs or simple data transfer objects if you like. Or you can pack it into a hashmap, or construct it into an object.

Clean Architecture p207

So, sure, if you like.

But I strongly suspect what's really bugging you is this:

enter image description here

This cute little abuse of UML contrasts the direction of source code dependency with the direction of flow of control. This is where the answer to your question can be found.

In a using relationship:

enter image description here enter image description here

flow of control goes in the same direction the source code dependency does.

In a implementing relationship:

enter image description here enter image description here

flow of control typically goes in the opposite direction the source code dependency does.

Which means you're really looking at this:

enter image description here

You should be able to see that the flow of control is never going to get from the Presenter to the View.

How can that be? What does it mean?

It means the view either has it's own thread (which is not that unusual) or (as @Euphoric points out) flow of control is coming into the view from something else not depicted here.

If it's the same thread then the View will know when the View-Model is ready to be read. But if that's the case and the view is a GUI then it will have a hard time repainting the screen when the user moves it around while they wait for the DB.

If the view has it's own thread then it has its own flow of control. That means to implement this the View will have to poll the View-Model to notice changes.

Since the Presenter doesn't know the View exists and the View doesn't know the Presenter exists they can't call each other at all. They can't fling events at each other. All that can happen is the Presenter will write to the View-Model and the View will read the View-Model. Whenever it feels like it.

According to this diagram the only thing the View and the Presenter share is knowledge of the View-Model. And it's just a data structure. So don't expect it to have any behavior.

That might seem impossible but it can be made to work even if the View-Model is complex. One little incrementing update field is all the view would have to poll to detect a change.

Now of course you can insist on using the observer pattern, or have some frameworky thing hide this issue from you but please understand that you don't have to.

Here's a bit of fun I had illustrating the flow of control:

enter image description here

Note that whenever you see the flow going against the directions I defined before, what you are seeing is a call returning. That trick won't help us get to the View. Well, unless we first return to whatever called the Controller. Or you could just change the design so that you can get to the view. That also fixes what looks like the start of a yo-yo problem with Data Access and it's Interface.

The only other thing to learn here besides that is that the Use Case Interactor can pretty much call things in whatever order it wants as long as it calls the presenter last.

نصائح أخرى

I find this problem too confusing and it would take lots of text and time to properly explain the problem as I believe you misunderstand both Martin's Clean Architecture and MVVM.

First thing to note, is that the diagram you posted is incomplete. It only shows "business logic", but is missing some kind of "orchestrator" that actually makes the parts move in the right order. enter image description here

The code of orchestrator would be as simple as

string Request(string request) // returns response
{
    Controller.Run(data);
    Presenter.Run();
    return View.Run();
}

I believe I heard Martin talk about this in one of his talks about Clean Architecture.

Another thing to point out is that candied_orange's remark about lack of cycles is wrong. Yes, the cycled don't exist (and shouldn't) in the architecture of the code. But cycles between runtime instances are common and often lead to simpler design.

That is case in MVVM. In MVVM View depends on ViewModel, and ViewModel uses events to notify View about it's changes. This means that in the design of the classes, there is only dependency from View to Model classes, but during runtime, there is cyclical dependency between View and ViewModel instances. Because of this, there is no need for orchestrator, as ViewModel will provide View way to figure out when to update itself. This is why "notifications" in this diagram use "squigly" line and not direct line. It means View observes changes in ViewModel, not that ViewModel depends on View.

enter image description here

The most important thing you should take from Martin's Clean Architecture is not the design itself, but how you handle dependencies. One of the critical points he makes in his talks is that when there is a boundary, then all code dependencies crossing that boundary cross it in single direction. In the diagram, this boundary is represented by double line. And there is lots of dependency inversion through interfaces (InputBoundary, OutputBoundary and DataAccessInterface) that fixes the code dependency direction.

In contrast the ViewModel in Clean Architecture is just plain DTO with no logic. This is made obvious by <DS> tag. And this is reason why orchestrator is necessary, as View won't know when to run it's logic.

If I were to "flatten" the diagram into what will it look like during runtime, it will look like this:

enter image description here

So during runtime, the dependencies are in "wrong" direction, but that is fine.

I recommend watching his talk about Clean Architecture to better understand his reasoning.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى softwareengineering.stackexchange
scroll top