.NET can has wow front-end too
Recently we successfully integrated our front-end approach into a .NET setup. We created a demo viewer, defined a suitable project structure, and shared our code for it on Github:
In this blog post, we explain why and how we set it up.
Why we need a .NET front-end stack
Node.js on the back-end is still a fairly small affair, moreso in corporate environments where .NET and Java still reign supreme. However, as front-enders we prefer to work with Node.js — or, to be honest, we need to, as many of the tools we use are written with Node.
We often try to combine the front-end and back-end into a single project environment. If for some reason we can’t, we deliver a static, modular front-end using Nunjucks templates and stub data. Back-end developers then build the actual application out of prerendered templates and precompiled assets.
This conversion however is time-consuming and prone to errors. So, to smoothen this process, we built a front-end development stack for .NET.
Running .NET on macOS
ASP.NET is available for Windows only, whereas all of De Voorhoede (and many front-end developers in general) run macOS. The solution: .NET Core.
.NET Core is fast, open source and targeted at web development: a perfect match. With it, we are able to use our beloved front-end tools for generating assets (such as images and style sheets) and use .NET’s Razor templating language.
Picking best practices
We looked into our collection of front-end best practices and made a list of what to put in our new .NET stack.
- Module based development: Keep styling, tests, README’s and templates together in the same directory
- Live reload: When we make changes in our code, the browser immediately updates the result
- Template building: The ability to create and maintain templates that are compatible with the .NET codebase
- Module demo viewer: One of the best parts of our front-end approach is the ability to preview modules individually and in different states.
A back-end agnostic module viewer
First, we created a module viewer that was back-end agnostic. Instead of feeding it with prerendered HTML, we let it consume a list of paths to all existing modules. These modules are then rendered server side using — in the case of .NET — a ViewModel and stub data. This has two major advantages:
- We are building something that already works in a real environment
- We can now use this viewer with any server side framework, like Python’s Flask or Node’s Express.
After that, we replicated our existing module based structure adhering to the .NET MVC file structure.
Embracing .NET and its standards
To make sure we fall in line with the .NET way of things, we had to keep in mind these few differences from our trusted Node environment:
- In .NET, naming is done in
- We usually use a “View” to build up a webpage out of modules. In .NET, we use an “Areas” instead
- It‘s important to understand ViewModels and the way they work
- There is a
Shareddirectory, which is the fallback folder for templates that aren’t found anywhere else by the render engine
With all this in mind we created the following project structure, where
PasswordToggle is a module:
/ ├── ViewModels ├── Views │ ├── Home │ │ ├── Index.cshtml │ └── Shared │ │ └── PasswordToggle │ │ ├── PasswordToggle.cshtml │ │ ├── PasswordToggle.demo.cshtml │ │ ├── PasswordToggle.css │ │ ├── PasswordToggle.js │ │ ├── PasswordToggle.test.js │ │ └── README.md └── wwwroot
What each file represents:
.cshtmlfile is the partial that is requested by the Areas
.demo.cshtmlfile is a demo implementation of the module. In it, the partial is imported. Using different settings and stub data we can demonstrate the module’s various uses
.cssfile contains all the styles relative to the module. It’s picked up during the build process, so we can use PostCSS
.jsfile contains all interactive UI behaviour of a module. A build task compiles all these scripts to .NET’s
- A unit test can be written in the
- The module’s Markdown-formatted documentation is in the
README.md. This documentation is available alongside the preview in the demo viewer.
This structure, plus the definition of ViewModels for each module, allows us to define a “contract”. With back-end developers we can then agree on required data to populate the modules.
Using this project structure and the module viewer, anyone can interact with all parts of the existing UI. Moreover, we can start building usable templates without waiting for the back-end to have real data. And as soon as the back-end is ready, we can immediately use these templates without having to copy them to another environment.
Special thanks to João Antão for helping us with the .NET Core concept and setup.