In MVVM should the ViewModel or Model implement INotifyPropertyChanged?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
In MVVM, the usual default is that the ViewModel implements INotifyPropertyChanged because it is the layer directly responsible for UI-facing state. The Model may implement it too, but that should happen only when the model itself is intentionally observable, not because the UI happened to need notifications today.
Why the ViewModel Usually Owns It
The ViewModel sits between the View and the domain model. It exposes bindable properties, formats values for display, and coordinates commands. Those are exactly the responsibilities that make INotifyPropertyChanged a natural fit.
When the UI binds to CustomerName, IsBusy, or CanSave, it is usually binding to ViewModel properties, not directly to persistence or domain objects. That is why ViewModels nearly always implement the interface.
Keep the Model Focused on Domain Concerns
A model should represent business data and rules. If every model object implements UI-oriented notification interfaces by default, the domain layer starts carrying presentation concerns it may not actually need.
That does not mean models must never implement INotifyPropertyChanged. It means the decision should come from the model’s own usage pattern, not from habit.
When the Model Implementing It Is Reasonable
There are legitimate cases where the model itself is observable:
- the model is editable state used directly across several ViewModels,
- the domain object is intentionally mutable and must notify changes to many consumers,
- the application uses rich client-side editing over long-lived domain objects.
In those cases, having notifications on the model can reduce duplication. But it is still a design choice, not an MVVM rule.
A Common Hybrid Pattern
A very common pattern is:
- Model contains domain data and business rules,
- ViewModel implements
INotifyPropertyChanged, - ViewModel wraps or projects model values into bindable properties.
This keeps the binding contract in the ViewModel while still storing the real business data in the model.
Ask What the Model Is For
The right question is not "which layer is allowed to implement the interface?" The better question is "does this object need to be observable as part of its own responsibility?" If the answer is only "because the UI wants to bind to it," the ViewModel is usually the better place.
Avoid Dogma in Shared Editable Models
In some applications, the same model instance is edited from multiple screens or reused across a workflow where direct change notification has real value. In that case, letting the model implement INotifyPropertyChanged can be justified. The mistake is not the interface itself. The mistake is adding it by reflex instead of because the object genuinely needs to be observable in its own right.
Common Pitfalls
- Putting
INotifyPropertyChangedon every model class automatically without a design reason. - Forcing ViewModel logic down into the model just to satisfy binding.
- Assuming the model must never implement notifications, even when it is a long-lived editable object.
- Binding directly to domain models and then losing a clean presentation boundary.
- Treating MVVM as a rigid rulebook instead of a separation-of-concerns pattern.
Summary
- In MVVM, the ViewModel is the usual place for
INotifyPropertyChanged. - The Model may implement it when the model itself is intentionally observable.
- The default goal is to keep UI-binding concerns out of the domain model unless there is a clear benefit.
- Wrapper ViewModels are a common and clean compromise.
- The best choice depends on object responsibility, not on a blanket architectural slogan.
Related reading
- In paxos, what happens if a proposer is down after its proposal is rejected?
- In Paxos, why can''t we use random backoff to avoid collision?
- In Raft distributed consensus, what do I set votedFor to?
- In RAFT is it possible to have a majority consensus on a log entry but the entry is not committed?
- In tensorflow distributed mode, there is something weird run in one ps - one worker
- Inconsistent cache values using Zend Cache with AWS ElastiCache across multiple servers
- Increase number of shards in DynamoDB to spin up more lambdas in parallel
- Increase Throughput of Debezium Kafka Connector

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.