The Week I Learned MVVM the Hard Way
When I started my latest JUCE project, I thought I had this.
I knew C++, I knew audio, I knew Max/MSP… but JUCE? Not so much. I picked this project to learn JUCE, not how quickly I could break my own code.
At first, it was fine. I could make things play, tweak a button, feel like a wizard. But then I started adding features. One, two, five… and suddenly, my code was a nightmare.
No consistent UI patterns. Features were impossible to add without breaking something else. Bugs multiplied like gremlins in a hot kitchen. Every new line of code was a gamble.
I didn’t know it at the time, but I was staring down the dreaded week-long refactor.
That’s when I called in Chaddie. He looked at my chaos and said three words that changed everything: “Use MVVM yo!”
So What Exactly Is MVVM, and How Does It Apply to JUCE?
If you’re new to this, MVVM stands for Model-View-ViewModel. It’s a design pattern that helps separate your code into clean layers, making it easier to maintain, test, and scale. Think of it as organizing your kitchen: everything has its place, so you don’t trip over pots and pans while cooking.
In JUCE terms:
- Model (Engine): This is your core logic—the audio processing, DSP, data handling. It doesn’t care about buttons or sliders; it just does its job.
- View: The UI stuff—buttons, knobs, screens. This is what users see and touch. It can change styles, layouts, or even go full disco mode without affecting the rest of your app.
- ViewModel: The middleman. It translates between the Model and View. User clicks a button? ViewModel tells the Model. Model has new data? ViewModel updates the View. It’s like a translator at a party where everyone speaks different languages.
Why JUCE? JUCE apps can get messy fast with UI tied directly to logic. MVVM keeps things decoupled, so you can swap out interfaces (e.g., add MIDI control or touch gestures) without rewriting your audio engine. It’s not JUCE-specific, but it fits perfectly for building robust plugins or apps.
If you want a deeper dive, check out this excellent video on MVVM in JUCE—it’s beginner-friendly and shows real code examples.
A Quick Mock Directory Structure
Here’s how I organize MVVM in my JUCE projects (keeping it simple):
src/
├── Engine/ # Model - Core audio logic
│ ├── AudioProcessor.cpp
│ └── DSP.cpp
├── ViewModel/ # ViewModel - Translators and state
│ ├── MainViewModel.cpp
│ └── ParameterViewModel.cpp
└── View/ # View - UI components
├── MainComponent.cpp
└── CustomKnob.cpp
This structure keeps things separated—Engine handles sound, ViewModel manages state, and View draws the interface. Easy to test, easy to change.
At first, I thought he was joking. MVVM? Sounds fancy. But as we mapped it out, it clicked:
- Engine: does its thing, audio plays, logic works.
- ViewModel: the translator, the unsung hero. Touch input? MIDI? Swipe? It doesn’t matter—the engine doesn’t care.
- View: can change, move, animate, go full disco mode. Engine and ViewModel don’t even flinch.
It was beautiful. It made sense. It made my sanity feel like it was slowly coming back.
Lesson learned: every new JUCE project starts with MVVM, day zero. Not day one. Not after the first line of code. Day zero.
Because the moment you write a line of code without it, you’re basically signing up for a week of frustration, chaos, and debugging spirals that make you question all your life choices.
From now on, MVVM isn’t optional. It’s my first line of defense, my sanity insurance, my secret weapon for building things that don’t make me want to throw my computer out the window.
And honestly? It feels damn good to finally be building, not patching.