Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Swift

MVVM-B - B from Business logic

5.00/5 (15 votes)
14 Mar 2022CPOL11 min read 12.6K  
Mistakes that can be made while using MVVM and how to overcome them
This article discusses some mistakes that people make when using MVVM, and how to overcome them, independently if the error is MVVM's fault or just a misunderstanding on how to apply it.

Background

I have a friend that's studying how to develop iOS apps. In one of our many conversations, he told me he is using MVVM. I actually didn't know MVVM was being used for iOS development and, out of curiosity, I decided to search on the internet about iOS MVVM Best Practices.

I was surprised to see how much bad material I found. In one case, the article explained correctly about Model, View and ViewModel... but continued by showing an example that had just a View and a ViewModel. The Model was completely forgotten, and my thinking was "how will anybody learn about the model, if the example completely ignored it with no explanation about why?"

In other cases, the authors stated that the ViewModel is the place to put our Business Logic. And I was like "Whaaat???".

So, this article is all about trying to give examples and justify the logic behind MVVM, instead of just trying to tell you "what to do" to follow the pattern.

Before Continuing

In this article, I will focus on some problems that I see when people try to blindly follow MVVM.

I am not an iOS developer and will not be showing Swift code, even though my inspiration came from iOS articles.

In fact, after my initial search on iOS, I looked for WPF articles, and also saw many "oddities" with MVVM. So, I am trying to focus on the main idea.

I honestly don't know what the original MVVM thinking was, but I know for sure that I used a similar pattern way before MVVM was a thing, and so I will be using that to explain MVVM, or at least the MVVM-B, which is definitely my interpretation on MVVM.

The Basics - MVVM means Model-View-ViewModel

If we are talking about a database app, Model is probably the data that we will be reading from the database and editing.

View is the Window or Control that we will show on the screen to be able to both visualize and edit the contents of the Model.

And the ViewModel is there to "help" the View and the Model talk. I will be honest, I think in many cases the ViewModel is excessive and the View could talk to the Model directly. Yet, as a pattern, the ViewModel is always there. Also, if I were to "kill" one of the classes because I can simplify things, I would kill the ViewModel, never the Model.

Do You Spot the Problem?

OK... this is an unfair question. People might see so many different issues that I can't expect to see an answer that matches my current thinking.

The problem I am seeing so far is that MVVM is all about creating that synchronization between a View and a Model, using a ViewModel as a helper, but it doesn't talk about any business logic.

In this article, MVVM-B is all about the Business Logic. Maybe I am just explaining what MVVM was always meant to be. Maybe I am creating a secondary pattern (which would also justify the B at the end of the name). Anyways...

Some Accepted Solutions

As I see, the biggest issue with business logic with MVVM is that:

  • People coming from MVC say that the business logic was supposed to be in the controller originally, so it should be in the ViewModel now;
  • People coming from either simple OOP or rich-database objects say the Model needs to have the business logic and include methods like Insert(), Update() and Delete().

Also, on top of that, there is the new idea of using POJOs, POCOs, POSOs, etc, to which I even made a joke that we should take the programming language out of the equation and instead of saying an object is a Plain Old Java Object or similar, we should just say that they are POO (that is, Plain Old Object, with no programming language involved).

OK... I know POO sounds terrible in English, and so I will say those are just POxO, and the idea many people have is that we use simple objects instead of rich ones. That simple objects are passed around and given to other objects/methods that know what to do for real.

Anyways, are the Models Supposed to be POxOs?

When I first read about MVVM, they weren't. But with this "new understanding" that they need to be POxOs, it seems that the MVC like solution, where the ViewModel has the business logic, is the right answer.

So, instead of trying to agree with that, I want to explore the problem a little further.

Going Back to the Source of the Problem to Get an Answer

One of the most basic database examples I saw, both as a kid (when I was learning by myself) and at high school, was how to create a Code/Name table and then insert data in it.

I would have two textboxes in the screen, a "Save" button and its "OnClick" implementation would be like:

C#
  ExecuteQuery("INSERT INTO SomeTable(Code, Name) 
    VALUES (" + textBoxCode.Text + ", '" + textBoxName.Text + "')");

Such code would work fine as long as the user wrote what was really expected on those textboxes before clicking Save. Such a code was prone to SQL injection and many other things, like just failing if the user needed to use ' or " in the name.

Anyways, I don't want to deal with SQL Injection in this article. I just want to say that, assuming all the values were right, it would work, and the insert would happen.

I also want to say that a pattern like MVVM is not going to help with the SQL Injection problem. That's not the problem it is trying to solve.

So, what is MVVM actually solving?

Code Isolation

The answer is actually "code isolation". But do we know what this means before exploring the issue? Or how is this important?

If we just say "I followed the pattern, my code is correctly isolated" are we really solving an issue?

Different Apps Using the Same Classes - This Includes Unit Tests

As an answer to the previous questions, one of the main reasons people care about MVVM is because it makes the code more "testable".

But being "testable" is just another way of saying that the code is able run in a different situation than the "user entered info and pressed the Save button on the screen".

For example, I might decide to create a heavily database-bound app that has a view for every table that exists in the database and also have a Console app that reads a text file and imports thousands (or even millions) of records from it, using the same code to do the inserts.

That is, that code I just presented:

C#
  ExecuteQuery("INSERT INTO SomeTable(Code, Name) 
     VALUES (" + textBoxCode.Text + ", '" + textBoxName.Text + "')");

Will need to be executed by the Console app.

Do You See the Problem?

If you don't see the problem, I will explain. Those parts like textBoxCode.Text and textBoxName.Text mean we need to have two valid text boxes created, with their text filled, for things to work.

So, there's a chance we need to actually create the window, show it, put values on its text-boxes, to finally be able to insert a record.

For a console app that just imports data, isn't that too much?

Wouldn't it be simpler to just "do the insert" without dealing with visual controls?

The answer is: Yes - Let's make the insert happen in a separate function

So, instead of accessing the text boxes directly from the click event, we could have a function/method, like:

C#
void InsertIntoSomeTable(int code, string name)
{
  ExecuteQuery("INSERT INTO SomeTable(Code, Name) VALUES (" + code + ", '" + name + "')");
}
// Again, this code is susceptible to SQL injection, but I am just dealing
// with MVVM, not SQL Injection, in this article.

So, now, this function is what the Console app will call. It will not need access to any View (or view related object). As long as this code is "isolated" enough from the view, we can call it. Also, for the view itself, the "simplest/dumbest" solution would be to call this function like:

C#
InsertIntoSomeTable(int.Parse(textBoxCode.Text), textBoxName.Text);

And not only would this do the same work as before for valid values, it would cause an immediate error if the value in textBoxCode is not convertible to integer, instead of letting the database driver accuse the error.

Where is InsertIntoSomeTable?

In the original solution, the entire code existed as part of the View. It was the "OnClick" of the View that had the code to access the database.

Now, because we have a visual app (the one with the View) and a console app (that reads a text-file), we had to put part of the code "somewhere else".

That "somewhere else" is the common-code between the console app and the visual app. I don't care if you would call it "common" library, "businessLogic" library or similar. It just needs to exist as a separate thing from the visual app and the console app.

What matters is that this "common-code" is not exclusive to the visual-app. Talking about MVVM, that means this code cannot be part of the ViewModel, as a console app that just reads a text file is never supposed to deal with a ViewModel.

Notice that we still didn't reach anywhere close to the MVVM pattern. In any case, it is clear that the "common code" between visual apps and "import data console apps" should not live anywhere near the "visual" side of the app. That is, they should not live in the View or the ViewModel.

So, Where Exactly Do They Live?

I would say that they either live as "part" of the Model themselves (so, the Models are not just POxO objects), or they live as separate things, like in Business-logic classes. And this is not covered by MVVM on its own.

Going Forward

The current situation is:

  • We have only one table;
  • We have both a visual (GUI) app that inserts records in it, and a console/importer app that reads a text-file and insert records in it;
  • And we were able to make "some code" be shared between the two apps.

What we don't have so far is a Model or a ViewModel.

For just 2 fields, a Model might sound excessive. Yet, if instead of just 2 fields we had 50, it would probably make more sense to call InsertSomeRecord(record) than calling InsertSomeRecord and passing 50 arguments.

Also, depending on how this is done, instead of InsertSomeRecord(record); we could be calling record.Insert();.

That means we would create a record, fill its fields and call Insert(); it doesn't matter if it is a console app or a visual app.

Isn't This a Model?

Before I go too far away from the main topic... can you see how we are just creating a Model class?

The Model actually has all the data that we save to a database... and the required Insert() method. Such a class can be used by both the Visual APP and the Console APP. And talking about MVVM, we reached the point where we have a View from one side and the Model from another... we just need the ViewModel now.

But, most importantly, did you notice that Business Logic is already here, and not part of the View or the ViewModel? That's the most important thing so far. We never want to put Business Logic in the ViewModel.

Finally, the ViewModel!

The only reasons for a ViewModel to exist is that either the data on the Model is not on the right format or the View has some extra traits that we don't want to save (into the Model).

For example, the ViewModel might include a background color that changes from time to time, that's not part of the View logic, but it is not part of the Model either.

The ViewModel might also be converting an enum to a string to be shown by the view. This case, in particular, seems to be irrelevant in frameworks like WPF, as we can use converters (and create default converters) and the like, than depending on a ViewModel, but they might be more useful in other environments, which seems to be the iOS/Swift case, but even that might be questionable.

The Other Important Thing is: Actions!

WPF uses the concept of commands, which doesn't seem to apply to iOS.

In any case, all of those could become "methods" of the ViewModel. But this doesn't mean we are putting "Business Logic" into the ViewModel. The ViewModel should be just "redirecting" to other classes instead of becoming the class with the business logic.

So, as a pattern, we have:

  • Model - Maybe a POxO object;
  • View - The actual thing shown on your screen;
  • Business - The code that makes your model go to the database, or that creates a model from the database - can be separated or part of the Model;
  • ViewModel - Just a helper to put model things into views, view things into models, and maybe call some Business methods, without being the business object.

This would create an MV-B-VM pattern but, as MVVM is so well known, better name it MVVM-B. Meaning Model-View-ViewModel-Business.

History

  • 12th March, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)