The case for any change of practice is made by identifying the pain points created by the existing design. Specifically, you need to identify what is harder than it should be because of the existing design, what is fragile, what is breaking now, what behaviors can't be implemented in a simple manner as a direct (or even somewhat indirect) result of the current implementation, or, in some cases, how performance suffers, how much time it takes to bring a new team member up to speed, etc.
Second, working code trumps any arguments about theory or good design. This is true even for bad code, unfortunately. So you're going to have to provide a better alternative, which means you, as the advocate for better patterns and practices, will need to refactor to tease out a better design. Find a narrow, tracer-bullet style plane through the existing design, and implement a solution that, perhaps, for iteration one, keeps the god object implementation working, but defers actual implementation to the new design. Then write some code that takes advantage of this new design, and show off what you win because of this change, whether it's performance, maintainability, features, correction of bugs or race conditions, or reduction of cognitive load for the developer.
It's often a challenge to find a small enough surface area to attack in poorly architected systems, it may take longer than you'd like to deliver some initial value, and the initial payoff may not be that impressive to everybody, but you can also work on finding some advocates of your new approach if you pair on it with team members that are at least slightly sympathetic.
Lamenting the God Object only works when you're preaching to the choir. It's a tool for naming a problem, and only works for solving it when you've got a receptive audience that's senior and motivated enough to do something about it. Fixing the God object wins the argument.
Since your immediate concern appears to be executive buy-in, I think you're best off making a case that replacing this code needs to be a strategic goal and tie those to the business objectives that you're responsible for. I think you can make a case that you can provide some technical direction by first working on a technical spike on what you think should be done to replace it, preferably involving resources from one or two technical people that have reservations about the current design.
I think you've found enough resources to justify your argument; people in such meetings will only pay attention to summary of your research, and they'll stop listening after you mention two or three corroborating sources. Your focus initially should be in getting buy-off to work the problem you see, not necessarily proving someone else wrong or yourself right. This is a social problem, not a logical one.
In a technology leadership role, you need to tie any of your initiatives to business goals, so the most important thing for making your case to executives is what the work will do for those objectives. Because you're also considered the "new guy," you can't just expect people to throw away their work or expect to rapidly fall in line; you need to build some trust by proving that you can deliver. As a long term concern, in a leadership role, you also need to learn to become focused on results, but not necessarily be attached to the specifics of the outcome. You're now there to provide strategic direction, remove tactical obstacles from progress by your team, and offer your team mentorship, not win battles of credibility with your own team members.
Making a top-down decision will rarely be credible unless you have some skin in the game; if you are in a similar situation all over again, you should focus more on consensus building within your organization rather than escalating once you feel the situation is out of control.
But considering where you are now, I'd say your best bet is to argue that your approach will bring measurable long-term benefits based on your experience and that it's in line with the work by well-known practitioners like uncle Bob and co., and that you'd like to spend a few days/weeks leading by example on a narrow refactoring of the highest bang-for-buck aspect, to demonstrate what your view of good design should look like. You'll need to align whatever your case is to specific business goals beyond your personal preferences, however.
Is this the same thing as the 'ViewModel' from the Model-View-ViewModel (MVVM) design pattern
Nope.
That would be this:
That has cycles. Uncle Bob has been carefully avoiding cycles.
Instead you have this:
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:
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:
flow of control goes in the same direction the source code dependency does.
In a implementing relationship:
flow of control typically goes in the opposite direction the source code dependency does.
Which means you're really looking at this:
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:
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.
Best Answer
DTOs are not part of the domain model, and do not have any behaviour. DTOs represent the data structures that are transferred across your system boundary, e.g. data that is serialized to be sent across a network. DTOs are also great for representing incoming data that is possibly inconsistent, before it is validated and translated to your domain model.
Within the domain model, the objects representing the concepts within the problem domain do typically contain associated business logic. The interesting part isn't behaviour that queries the state of the model, but methods that change the model, beyond simple setters. This can lead to a very elegant mapping of the problem domain into your code because the model basically manages itself.
Sometimes we don't want to do that, and keep most behaviour out of the immediate model. This is called an anemic domain model, and is a more procedural than object-oriented design technique. As such, it is considered to be an anti-pattern by some. However, this works very well if there is a lot of business logic that doesn't clearly belong to a single domain model object. Instead, we can structure the business logic by use case. Each use case then manipulates the domain model. The drawback is that it becomes more difficult to keep the model consistent.
These two approaches are not exclusive, but are a sliding scale. At one end, all behaviour is directly in the model, at the other the domain model consists of dumb records. In the middle, the domain model contains generic operations that ensure consistency, but specialized logic lives outside in services which orchestrate changes to the model.
In your case, an
isMarried()
accessor has nothing to do in a DTO. A DTO can just expose its data. Within your domain model, it seems that such a method clearly belongs to some kind of Person class. If you are introducing Util classes that are named after another class, that is a clear warning sign that your design might be off: in many cases that behaviour should be part of the original class or else should be modelled separately as it's not part of the modelled problem domain.However, I think that an
isMarried()
accessor is a weak example of this because it's basically just a getter, and a getter is basically just fancy syntax for a field. As a different example, let's consider a web application that allows students to enrol in lectures. If this is directly part of the domain model, we would see method calls likestudent.enrolIn(lecture)
orlecture.enrol(student)
in our code. But sometimes enrolment isn't simple and has complex business rules, e.g. how limited seats are given to the applying students. If enrolling is a whole process of its own, we might want to model that process as a separate object:lectureEnrolmentProcess.run(lecture, student)
. Coming back to yourisMarried()
behaviour: since this behaviour likely isn't a complex process with its own concerns, it shouldn't be kept separate.So I think there should be a clear bias to put most behaviour directly into the model, but we shouldn't be afraid to extract any behaviour that has an entirely different concern than the concept being modelled.