← All work CODE · 2026

ConsoleMVC - Bringing Model-View-Controller to C# Console Apps

My very first made and published NuGet package!

An MVC framework for console applications, inspired by ASP.NET Core MVC. Controller-ViewModel pattern with convention-based routing and Razor-like .cvw view templates.

Console Applications Deserve Better

At some point, most developers end up dealing with a console application that has grown far beyond what it was ever meant to be. Sometimes it is an old internal tool. Sometimes it is something you wrote yourself and now regret opening again. Usually it lives in one oversized Program.cs file, with a long while(true) loop, a pile of switch statements, and very little structure holding it together.

I have run into that kind of code more than once.

The usual reaction is to accept it, add another case, and move on. The assumption is always the same: it is just a console app. It does not need proper architecture.

I think that assumption is wrong.


The Problem With How Console Apps Are Treated

Web developers get a lot for free. In ASP.NET Core MVC you get controllers, routing, model binding, middleware, dependency injection, and a clear project structure from the beginning.

Console developers mostly get Console.WriteLine() and a blank file.

There is a real bias in how different types of software are treated. Web apps are seen as serious. Mobile apps feel current. Desktop apps are older, but still respectable. Console applications are usually treated like temporary scripts that do not deserve the same level of care.

I do not agree with that. ConsoleMVC is my attempt to prove that console applications can benefit from proper architectural thinking too.

The Idea Behind It

Once I started thinking about what a console screen actually does, the architecture became much clearer. A screen takes input, shows output, and then passes control to whatever comes next.

That is not so different from a web page. It may be simpler visually, but the responsibilities are similar. A console screen should focus on one job and not try to handle everything at once.

From there, the rest followed quite naturally. If web applications can separate controllers, views, and models, then console applications can do something very similar. The idea already existed. It just had to be adapted to the terminal.

That was the starting point for ConsoleMVC.

Convention Over Configuration

The first version of ConsoleMVC required manual registration for every controller. It worked, but it got tedious quickly. Once the number of controllers started growing, it became one more place to make mistakes, especially with naming.

So I removed that requirement.

The framework now discovers controllers and views automatically at startup using reflection. If you create a class called HomeController in the Controllers/ folder, it gets picked up. If you add a file called IndexView.cvw under Views/Home/, the router finds it. There is no separate registration step.

Here is an example GreetController from the ConsoleMvc v1.1.0 template package. It displays the Index page, receives form data posted from the view, and shows a personalised greeting. If the name is empty, it redirects back to the form.

That makes the framework feel lighter to use. As long as the naming conventions are followed, things work without extra setup. That is exactly how I wanted it to feel.

The Source Generator

The .cvw view files are the part of ConsoleMVC that took the most work.

The goal was to let developers write plain C# inside a file with an @model directive at the top, then have that file compiled into a proper ConsoleView<TModel> class at build time. I did not want runtime string evaluation or loose dynamic behavior. I wanted it compiled, typed, and checked properly.

Getting there was not straightforward.

In its current form, it is still quite simple. A .cvw file is really just a custom file type with some extra syntax and plain C#. That also means the editing experience depends a lot on tooling. If you are not using Rider, the experience is noticeably worse because there is no proper language support by default. That is why I also built a custom ConsoleMVC CvwSupport plugin to provide basic support for working with these files.

Roslyn source generators are powerful, but they are also very strict. The generator targets netstandard2.0, so some APIs that would normally be convenient are not available. You find that out quickly when the compiler starts complaining.

Still, when it works, it feels good. Writing something like @Model.Title in a .cvw file, getting autocomplete, building successfully, and seeing the right output appear in the terminal makes the whole system feel worth it. That kind of experience is exactly what I wanted the framework to provide.

Form Posting in a Console App

One feature I was not fully convinced by at first was form posting. The idea is simple: a view collects input, puts it into a Dictionary<string, string>, and posts it to a controller action. The framework then binds the values to the action parameters, similar to how web MVC handles form posts.

At first I thought this was unnecessary. It seemed easier to just read input directly inside the controller.

After working on it more, I changed my mind.

Keeping input collection inside the view and business logic inside the controller is exactly the separation MVC is supposed to give you. Once you start mixing those responsibilities, you slowly slide back toward the huge Program.cs file that caused the problem in the first place.


// Post individual values to the Result action.
// The framework binds each dictionary entry to the matching
// parameter name on CalcController.Result(int a, int b, string op).
var formData = new Dictionary<string, string>
{
    ["a"] = a,
    ["b"] = b,
    ["op"] = op
};

The model binder supports simple parameters, complex types, nullable values, enums, GUIDs, and case-insensitive key matching. Missing values are handled safely. Type conversion failures are handled safely too. A lot of that came from seeing where the rough edges were and fixing them one by one.

What's Coming Next

Right now, ConsoleMVC already has a working foundation. The next part is what interests me most: C-View Markup.

The plan is to create a markup language designed specifically for terminal interfaces. Instead of forcing developers to manually build layouts with raw console output, the framework would offer tags like <box>, <table>, <menu>, and <input>. It would not try to imitate the browser. It would focus on the kinds of layout primitives that actually make sense in a terminal application.

I also want Razor-style @ expressions for embedded C# logic, along with styling options for color, alignment, and borders. The source generator would compile all of this into rendering code at build time.

The general idea is simple: building a console screen should feel closer to building a view, and less like manually arranging text output line by line.

Console Apps Deserve Better

ConsoleMVC is still early. There are missing features and a fair number of rough edges. I am building this as a student because I think the problem is real and I do not think the existing tooling solves it well.

Even so, the foundation is already solid. Convention-based routing, reflection-based discovery, a compiled view engine, and automatic model binding all bring the same kind of structure that makes web frameworks productive.

Console applications are not lesser applications. They are applications that have usually been given less attention, less tooling, and lower expectations than they should have been.

ConsoleMVC is my attempt to challenge that.


The project is open source and available on NuGet as ConsoleMVC.Framework. If you have experience with frameworks, rendering engines, or markup parsers and want to share thoughts on the direction, especially around the C-View Markup roadmap, I would genuinely like to hear them. Open an issue, send an email, or have a look through the code.