Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / Swift

MVVM-B - B from Business logic

Rate me:
Please Sign up or sign in to vote.
5.00/5 (15 votes)
14 Mar 2022CPOL11 min read 11.3K   13   2
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)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions

 
QuestionExpanding on this Pin
NeverJustHere16-Mar-22 18:53
NeverJustHere16-Mar-22 18:53 
Hi Paulo,

Nice summary, but I think where people start mixing things up with this is where the requirement for rich view functionality needs additional knowledge of Business Logic.

Simple example is how long can text in a particular attribute be.

As you've said, it is the responsibility of the model to validate/enforce this, but if we can map this through to a UI control, then we have a better UI.

As I've thought about this, I've broken Business Logic into categories (what's missing?):

- Absolute Constraints - Nullablity, Max lengths, Min/Max values but also cardinality relationships between entities. i.e. Is this set of values a valid entity?

- Transition Constraints - Can this entity move from here to there? Can a delivered order be cancelled? Can a payment rejected order be dispatched? These are the workflow rules common in business applications.

- Permissions - Can this user make this change or see this record?

- Business Actions - when this happens, these other actions are also needed. When an order is created, it needs to create a task record, or send an email or whatever.

Much of this can be represented by data in the model, that is made available to the view so that the view can behave accordingly.

Absolute Constraints are often represented as property attributes on the POxO objects - true meta data.
Transition Constraints are often represented as data, a table of status's and valid steps that can be taken, or even as a full Finite State Automata. The viewmodel should be making this available to the view as required. Thus, only valid buttons or options in a dropdown are visible.
Permissions, likewise, are data that the view model should present to the view.

Business Actions are the one thing that may not need to be visible to the view.

Does this seem sensible? And is this worth expanding?
AnswerRe: Expanding on this Pin
Paulo Zemek16-Mar-22 21:07
mvaPaulo Zemek16-Mar-22 21:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.