Over the past decades designing an application has gone through several overhauls. Of course whether this affects you or not depends on if you’re into doing the latest thing or if what you already have can in itself withstand the changes in computing landscape. But if you’re like me and at least like to entertain the idea of using new tech when starting a new project, getting familiar with the overall design pattern is useful. Here’s a few examples from the past, present and a proposal for the future.
C a controller-only pattern. Ironically this was common with early C language. Purely utilitarian programs, designed to do a single thing, often working with standard input and providing standard output.
VC View Controller only. One might say what good is having just a pretty interface and some functionality but no solid data model underneath? A lot of Web 1.0 worked like this – HTML providing the view and some C doing the C. Ironically this was enough to attract VCs in time for the .com bubble.
MVC Some could say Model-View-Controller has been the schoolbook model for a “modern” application design. It still holds up and works great for desktop, mobile and web apps alike. It forces you into good habits and separation of code in a way that doesn’t sacrifice performance or sanity. This one’s still a winner and probably the most common pattern out there.
MVCV A server-client application with a shared data model. More than an actual design pattern, it’s a symptom of (too) rapid development. There is usually something reminiscent of RPC in between if not even transmission of actual logic over the network (MV(CMC)CV). Run away.
MCVCMV Often what RESTful applications look like, intentionally or not. There’s a separate model on server and client side, a REST view, controller for server logic, client controller to funnel it to the local model and the final view on local end.
MCVCMCMV Like above, but with two data models for the client side, one for local storage and another for display and/or deltas to group changes together before sending back. This can make you question your sanity and will also make any potential problems really difficult to hunt down. You will want to push some serious TDD into this mix.
MCCCVMCCMVCV One could make the argument that this does not exist in the wild, but it does. In a project the business logic may be layered between teams and unit tested to exhaustion. If you throw in a REST API, client model and a user interface made from building blocks that are tied together from the final view controller, you’ll have something straight from science fiction.
MVVM The new hotness. React, Vue, Flutter, SwiftUI. It’s all centered around the idea that you’re just changing data and that has an effect. And just as it’s missing the C altogether, you can easily feel you’ve lost control. The actual code that changes the things you see is something you don’t write at all. if you’re comfortable with not knowing what’s going on, this is great for you.
However, I’ve never been comfortable with handing over the controller part to a third party. That is why my proposal for a new pattern for the year MMXXII is a spin on MVVM: MV:VC.
What is it? Why MV:VC?
1: What is MV:VC?
It’s exactly what it looks like. You’ve got a data model, separated from client operations using a view – REST or whatever you like – with proper authorization in place. On the client end the view controller uses the backend as a data storage and directly funnels that data to the view. Changes are atomic but asynchronous, offloaded to a thread that handles them in order.
2: Why MV:VC?
For starters; Internet is ubiquitous and fast enough for this. Solving the throughput problem is often easier and more straightforward than trying to keep a local data model in sync with the server data model. Also, there’s only one place to keep the business logic and changing it won’t require a server rollout. The operation queue can also be designed to keep track of dependencies so that operations depending on outcome of previous ones are simply unavailable to the user until the completion of dependencies.
3: Challenges of MV:VC and overcoming them.
Since there’s only one data model it needs to flex to fit both the needs of logic and display. A somewhat simple way to come up with that kind of data model is to start designing from top down:
- Start with what the user needs to do and see.
- Work your way down to what should happen and with what data.
- Sum those two into either data model fields or calculated values of said fields.
- Settle on a dependency structure – in a normal scenario it is very simple to determine a starting set of blocking events: “anything to do with a data entity that has a direct line of relationships to this entity will block changes to this entity”.
Keeping both parts in a single repository usually helps – if that’s not possible, then as submodules of a common parent repository. The tight coupling brings out the other challenge: since there’s only one model, both ends need to be consistently compatible. These principles will help with that:
- Always provide default values for data and reflect this in your migrations
- Old logic must not depend on new data
- Old logic must not be broken by new data
- New logic can depend on old data
- Always update server first
- If possible, have server auto-update outdated clients (mainly works with scripted languages) or provide a way of easily updating the client. Never require updating on the spot.
MV:VC isn’t really a new. It’s just a take on MVC, with the added : to clarify there’s a separation between server and client. The choice of transport layer doesn’t matter. Many systems have become very complicated, split into microservices, run in containers, but this doesn’t necessarily need to be reflected as increased complexity. If the layers you need for load-balancing and distribution and spinning up new seats are set up to be agnostic of what your software does, all that can be compressed to a “:”.