MVVM and Clean Architecture: Understanding Where Our Code Actually Belongs
When I first started learning software architecture, I thought it was mostly about remembering names: MVC. MVP. MVVM. Clean Architecture. Repository. Use Case. Domain. Data. But after working with real applications, I realized that architecture is not really about memorizing these names. The more important question is: When an application grows, where should each piece of logic actually live? That question is what helped me understand MVVM and Clean Architecture more clearly. This article is my attempt to explain both concepts in a practical way, without treating them as complicated rules or a collection of folders. When a Simple Application Starts Becoming Complicated Imagine we start with a simple expense-tracking application. At first, the application might only need: - A screen to enter an expense - A button to save it - A list showing expenses Everything feels simple. But then the application grows. Now we need: - Input validation - Different expense categories - API communication - Local database storage - Loading and error states - Authentication - Business rules - Offline support - Data synchronization - Reporting - Notifications - Analytics Suddenly, the screen that originally handled a few things is now responsible for many completely different concerns. The UI knows about the API. The API logic knows about the UI. Business rules are mixed with button-click logic. Database code appears inside presentation code. Changing one thing starts affecting several unrelated parts of the application. This is where architecture becomes important. Architecture Is About Responsibility One of the biggest things I learned is that architecture is not primarily about creating folders. You can create folders called: components services models repositories utils screens and still have a badly structured application. Architecture is really about responsibilities and boundaries. Every part of the application should have a clear answer to questions like: - What is this part responsible for? - What should it know about? - What should it not know about? - Who should it communicate with? - What happens if the API changes? - What happens if the database changes? - Can this business logic be tested without the UI? Once I started looking at architecture this way, MVVM became much easier to understand. MVVM: Separating What We See From What We Do MVVM stands for: Model - View - ViewModel The basic idea is simple. Instead of putting everything inside the UI, we separate the presentation responsibilities. Think about a screen that displays a list of expenses. The screen needs to: - Display the expenses - Show loading information - Display errors - React to user actions But does the screen itself need to know how the data is fetched? Not necessarily. That is where the ViewModel becomes useful. View The View is what the user interacts with. Its responsibility is mainly presentation. For example: - Screens - UI components - Buttons - Forms - Lists - Loading indicators - Error messages The View should focus on: What should the user see? It should not become the place where every application decision is made. ViewModel The ViewModel sits between the View and the rest of the application. It manages things such as: - UI state - User actions - Presentation logic - Loading state - Error state - Preparing data for the View For example, when a user taps Save Expense, the View does not need to understand everything involved in saving that expense. The View can simply communicate the action. The ViewModel can then coordinate what needs to happen. This gives us a useful separation: View = What the user sees ViewModel = What the UI needs to know and what should happen from a UI action Model The Model represents the data and concepts used by the application. Depending on how the application is designed, this can include things such as: - Data objects - Entities - Data structures - Related data concepts The exact meaning of "Model" can vary between different implementations of MVVM. That is important because MVVM itself does not define every detail of the entire application's architecture. And this leads to an important point. MVVM Is Not the Entire Architecture MVVM is mainly a presentation pattern. It helps us organize how the UI communicates with presentation logic. But what happens after the ViewModel needs to perform an actual business operation? For example: "Create this expense." Should the ViewModel directly call the database? Should it directly call an API? Should it contain all the business rules? As applications become larger, putting everything inside the ViewModel can simply create another problem: A giant ViewModel. This is where a broader architectural approach becomes useful. That is where Clean Architecture comes in. Clean Architecture: Separating the Core From External Details Clean Architecture is broader than MVVM. The main idea is to keep the important business logic of an application independent from external technologies and implementation details. External details can include things like: - UI frameworks - APIs - Databases - Network libraries - Storage systems - Third-party services The business logic should not become tightly coupled to these things. A common way to understand Clean Architecture is through three major areas: - Presentation - Domain - Data These are not simply folders. They represent different responsibilities and boundaries. Presentation Layer The Presentation layer is responsible for interacting with the user. This is where things such as: - Views - ViewModels - UI state - User interactions usually live. This is also where MVVM can fit naturally. So instead of thinking: MVVM vs Clean Architecture it is more useful to think: MVVM can organize the Presentation layer inside a Clean Architecture application. Domain Layer The Domain layer is the heart of the application. This is where we keep the important business concepts and rules. The Domain layer should not need to know whether the application is using: - REST API - GraphQL - PostgreSQL - MongoDB - SQLite - A particular UI framework - A particular network library The business rules should remain independent. Entities Entities represent important concepts in the application's business domain. For an expense application, an entity might represent an: Expense For a shopping application: Order For a banking application: Account These concepts exist regardless of which UI framework or database the application uses. Use Cases A Use Case represents something the application does. For example: - Add Expense - Delete Expense - Create Order - Cancel Order - Generate Report - Update Profile Instead of thinking about Use Cases as another complicated architectural term, I find it easier to think: A Use Case represents an application action or business operation. This gives the application a clear place for business logic. Repository Interfaces The Domain layer may need data, but it should not need to know exactly where that data comes from. For example, an Add Expense use case may need to save an expense. The Domain layer can depend on an abstraction such as a repository interface. It does not need to know whether the repository eventually saves the data to: - A remote API - A local database - Local storage - A cache - Some other data source That implementation detail belongs outside the Domain layer. Data Layer The Data layer deals with external data sources. This can include: - APIs - Databases - Local storage - Files - Caches - Repository implementations - Data source implementations For example, the Domain layer might say: "I need to save an expense." The Data layer handles how that actually happens. This separation becomes powerful when external technologies change. Suppose an application initially uses one database and later moves to another. If the business logic is tightly coupled to the original database, the change can become painful. But if the business logic communicates through abstractions, the implementation can change without forcing the business rules to understand the new technology. The Most Important Idea: Dependency Direction This is one of the most important ideas behind Clean Architecture. It is not enough to separate the application into Presentation, Domain, and Data. We also need to think about which layer depends on which. The important principle is: The core business logic should not depend directly on external implementation details. Think about a restaurant. The customer should not need to know: - Which supplier delivered the vegetables - Which brand of cooking oil was used - Which kitchen equipment was used - Where the restaurant purchased its ingredients The customer cares about the result. The restaurant's internal processes can change without changing what the customer expects. In a similar way, our business logic should not become tightly connected to external details. The database can change. The API can change. The UI technology can change. The core business rules should remain relatively stable. MVVM and Clean Architecture Together This is where the two concepts finally connect. They are not competing architectures. They solve problems at different levels. MVVM helps organize the presentation side. Clean Architecture helps structure the broader application and control dependencies between different parts. A simplified structure can look like this: Presentation │ ├── View │ └── ViewModel │ โผ Domain │ ├── Entities ├── Use Cases └── Repository Interfaces │ โผ Data │ ├── Repository Implementations └── Data Sources │ ├── API ├── Database └── Local Storage The important thing is not memorizing this diagram. The important thing is understanding why the separation exists. Following One Action Through the Application Let's follow a simple example. A user wants to add an expense. 1. User interacts with the View The user enters an amount and taps Save. The View captures the interaction. 2. View communicates with the ViewModel The ViewModel re
Comments
No comments yet. Start the discussion.