Choosing a “Modern” React.js + .Net Core Stack

This is really just preparation for a meeting tomorrow and I haven’t blogged much lately. Also, I had to write this way, way too late at night and the grammar probably shows that:/ I’m happy to take any kind of feedback, mockery, or questions.

One of our Calavista development teams and I are having a kind of kickoff meeting tomorrow morning to discuss our new technical stack that we plan to use for some big new customer-facing features in a large web application this year. The current application we’re building onto was originally designed about a decade ago and uses a lot of the .Net tools and approaches that I myself would have espoused at that time and even a few OSS things that I helped build — but some of which are clearly aluminum wiring today (RIP K. Scott Allen) or at least have been surpassed by later tools.

First off, we’ve got a couple constraints:

  1. .Net for the backend
  2. Sql Server as the database (but with perfect control on a greenfield project I’d obviously opt for Marten + Postgresql šŸ˜‰
  3. We can’t rewrite or update the very large application all at one time because of course we can’t
  4. The “new” stack still has to cooperate with the “old” stack
  5. Azure hosting
  6. Bootstrap styling

And, what we need and/or want in a new technical stack:

  • The ability to craft very good user experiences in the new features
  • Better testability all the way through the technical architecture. We have to depend much more on Selenium testing than I prefer, and I’d like to see much more of a balanced test pyramid / test funnel distribution of tests. At the bottom of this post is a brief explanation of why I’m leery of too much Selenium usage.
  • Good feedback cycles for the developers doing the front end work
  • Fairly mainstream tools as much as possible so our client can more easily deal with the system after we’re gone
  • A way to incorporate the new technology into the existing system with minimal disruption to the current state and without the users being aware of the technical sausage making (other than hopefully some better usability and richer features).

The Proposed “New” Stack

For a variety of reasons, we’re moving to using React.js for the client side backed by an ASP.Net Core “backend for frontend” service that will mostly be zapping JSON back and forth as an API.

On the server side, we’re going with:

  • ASP.Net Core 3.*. Probably with MVC Core for API endpoints, but I’m voting to use it in the lightest possible way. Our client wants to do this anyway, but I’ve been pushing it for awhile because of the easier build automation, testability, faster builds and throughput, and honestly because I want to code on OS X and not have to use my Windows VM;-)
  • I’m probably not willing to use any kind of “Mediator tool” because I think it’s unnecessary complexity, but we might pull in Jasper for any kind of asynchronous messaging or just plain asynchronous work through it’s local command bus
  • Entity Framework Core. Using Sql Server is a constraint, I’m not particularly a fan of Dapper, I don’t mind EF Core, it’s very commonly used now, and it’s what the development team wants to use. If we can get away with it, I want to use EF Core’s migrations support to create development databases on the fly for rapid prototyping and development throughput. If you’re old enough to remember that I was one of the authors of the EF Vote of No Confidence, I’d say that EF Core is a perfectly fine heavy ORM if that’s what you need and EF has left behind all the design decisions from EF v1 that we so strenuously disagreed with way back when.
  • We will also not be using any Azure tool that cannot be executed on a local developer box. So, using Azure Service Bus that you can connect to locally is fine, but weird serverless tools that can only be run on Azure are absolutely off the table as long as I have a say in anything.

The one place where we’ll deviate from what I guess is mainstream MVC Core is to absolutely ditch any kind of leftover Ruby on Rails Models/Views/Controllers folder and/or namespace layout. I’m very much in favor of using a “feature folder” organization where closely related classes/services/DTOs for a use case live in the same namespace instead of being scattered all over God’s green earth. Moreover, I’m dead set against any kind of “Onion Architecture” code organization, but that’s a rant for another day.

More interestingly, on the client side we’ve rewritten an existing feature in the application with a new “React Stack” that we plan to start with:

  • React.js vLatest. I’ve been mostly out of React.js for a few years, and I’ve been pretty happy with the improvements since I built Storyteller 3.0 with React v11. I really like React Hooks even though I didn’t really understand them well when they were brewing a couple years ago.
  • Parcel.js. ZOMG, Parcel.js is so much easier to get up and going that Webpack.js was a couple years ago. I’m absolutely sold. I think the hot module replacement ability in React.js is a killer feature and a huge advantage over trying to do complex user interfaces in MVC + Razor because of the vastly better feedback cycle, but it used to be a nightmare to get up and going with Webpack.js (IMO). It basically comes out of the box with Parcel.js.
  • React-Bootstrap. The existing application is based around Bootstrap anyway, and using this library instantly gives us a consistently styled application with the rest of the application. Plus it’s a pretty rich out of the box component library.
  • Redux and React-Redux for state management. I had good results with these tools before, and I don’t see any compelling reason to move to something else or to do without.
  • I don’t think we’ll use TypeScript, but I’m not opposed if the team wants to do that. I don’t see much advantage for React components, but maybe for Redux reducer code
  • I played some with Redux middleware and I’m at least intrigued by react-api-middleware, but we might just stick with simple axios usage.

More on the testing tools in a later section because that’s a crucial part of all of this.

 

Integrating the New with the Old

I’m going to stop using the word “microservice” with our client because that apparently conjures up visions of hopeless complexity. Instead, we’re starting a new stack off to the side for new features that may also become a strangler application that eventually takes over the existing functionality.

All the same though, there’s much less literature about microservices in an conglomerate user interface application than there is on backend services. We’re initially going down a path of running our new React.js feature bundles inside the existing application’s Razor layout in an architecture something like this:

 

ReactInExistingMVC5

For new features, we’ll keep to the existing navigation structure and application look and feel by just adding new Razor pages that do nothing but embed a small Razor application like so:

@page
@model Application.Feature1.ViewModel
@{
    ViewBag.Title = "Feature Title";
}

http://~/assets/feature_bundle.js
<link rel="stylesheet" type="text/css" href="~/assets/feature_bundle.css">

<div id="main"></div>

There’s some details to work out around security, API gateways, and the like — but the hope is to have the React.js mini-applications completely communicating with a new ASP.Net Core “BFF” API.

I’m hoping there’s not a lot of overlap in the necessary database data between the old and the new worlds, but I’m prepared to be wrong. Our current thinking is that we’ll *gasp* keep using the old database, but keep a new schema to isolate the new tables. Right now my working theory is that we’ll have a background queue to synchronize any “writes” to the new database schema to the old database if that proves to be necessary.

 

Testing Approach

Alright, the part of this that I’m most passionate about. I’ve written before about my personal philosophy for automated testing in Jeremy’s Only Law of Testing (Test with the finest grained mechanism that tells you something important) and Succeeding with Automated Integration Tests. I think that both React.js + Redux and ASP.Net Core have good testability stories, especially compared to the MVC5 + Razor stack we’re using today. React.js + Redux because there are great tools for fast running unit tests against the client side code that isn’t really feasible with Razor — and especially now that there are effective ways to test React components without needing to use real browsers with Karma (shudder). ASP.Net Core because you can run your application in process, there’s some decent testing support for HTTP contract testing, and it’s far more modular than ASP.Net MVC pre-Core was.

Looking at another low fidelity view of the proposed architecture just to demonstrate how the new testing pyramid should go:

TestApproachDotNetCore

We’ll probably use xUnit.Net instead of NUnit, but at the time I drew this diagram I thought we’d have less control over things.

With this stack, our testing pyramid approach would be:

  • Unit tests as appropriate and useful for the .Net code in the backend, with a focus on isolating business logic from infrastructure using techniques described in Jim Shore’s Testing Without Mocks article — which is a fantastic paper about designing for testability in my opinion.
  • Use Alba (wraps TestServer) to execute integrated HTTP tests against the entire MVC Core stack
  • We’ll probably use Storyteller for acceptance tests when we hit some very data intensive business logic that I know is coming up this year
  • Possibly use Docker to run little isolated Sql Server databases for testing something like this approach
  • At least smoke tests against the React.js components with Jest and react-testing-library (I think I came down on preferring its approach to Enzyme). I’m going to prefer some real unit tests on the behavior of the components using react-testing-library.
  • Jest unit tests against the state tracking logic in the Redux store and reducers.
  • Use moxios for testing the interaction with the backend from the JS code?
  • I had some success in the past writing tests that combined the Redux store, the react-redux bindings, and the React components in more coarse grained integration tests that flush out problems that little unit tests can’t
  • And just a modicum of new Selenium tests just to feel safe about the end to end integration between the React client and the ASP.Net Core server

 

 

Why Not….?

  • Angular.js? Ick, no.
  • Vue.js? I think Vue.js sounds perfectly viable, but the team and I have prior experience with React.js and the existing ecosystem of components matters
  • GraphQL? I don’t see it as applicable to this application
  • Alternative web frameworks in .Net Core? Not unless it’s my own;)
  • Dapper? Meh from me.
  • Blazor? This one is a little more serious conversation because the client asked us about it. My feeling is that it’s not quite ready for prime time, doesn’t have much of an ecosystem yet, nobody is familiar with it yet, and we’d lose that awesome hot module replacement feedback cycle in React.js

 

Why am I down on Selenium?

I spent all my summers in High School and College working on my Dad’s house framing crew. And as any framer knows, the “Sawzall” is the one tool that will always be able to get you out of a jam when you can’t possibly utilize any other kind of saw — but it’s never your first choice of tool and it generally only came out when something was put together wrongly.

sawzall

 

Selenium is the Sawzall of automated software testing (at least in web applications). It’s almost never the first choice of testing tool, but if you didn’t architect for testability, it’s your last resort because it can always work by driving the entire stack through the browser.

It’s also:

  • Slow to execute
  • Laborious to do well in more complex applications — especially when there’s asynchronous behavior in the user interface
  • Frequently flake-y because of the asynchronous behavior
  • Brittle when the user interface is evolving
  • Hard to debug failures because you’re testing the whole damn system

In summary, Selenium is what you use when nothing else can work but never your first choice of approach.

18 thoughts on “Choosing a “Modern” React.js + .Net Core Stack

  1. 1. Why not Azure Functions? Many benefits, not least Queue and Timer triggers.
    2. Assume you have a hard constraint on SQL server, but if running in Azure, Cosmos is amazing.
    3. I think we are at the point now where if you’re not using Typescript, you are doing it wrong (the only real argument for not using it is that you don’t know it)
    4. Whatever web framework you pick you’ll probably be throwing it away in a couple of years. Aurelia is the one that gets out of your way the most, is easiest to pick up and sticks closest to web standards.

    1. Let’s see:

      1. I don’t think we have any real need for that, and I’m honestly not that impressed with Azure functions personally
      2. Cosmos would be overkill in the near and medium future, and their existing Sql hosting and migration infrastructure is doing just fine
      3. I don’t particularly agree with you there, but I’m open to Typescript later on bigger SPAs
      4. I have worlds of respect for Rob Eisenberg, but Aurelia doesn’t have enough market share or user base to be viable for our client IMO.

  2. I’ve looked at MobX a couple times, but I just didn’t see any compelling reason to switch over. I think in this particular case that I like Redux’s more FPish approach. Especially when paired with immutable.js in bigger apps

  3. Totally with you on the features folder. A guy I worked with a long time ago introduced me to the concept (you’ve probably worked with him before) and I was pretty much sold. Also agree on foregoing the Onion Architecture. Was never a fan.

    You mentioned not using any sort of Mediator tool. What are you planning to do in lieu of that? Just doing the work in the controller method?

    Not saying this is wrong or bad mind you. I’ve been playing around with some other web frameworks that don’t really have the Mediator pattern and at a certain scale, and enough discipline, I think it would be perfectly fine. Also, doing straight up HTTP testing in these frameworks has been relatively easy too, which makes me wonder if the growth in the Mediator pattern was due to how difficult this was in the past on .NET.

    1. I can’t say this with any kind of authority because I just don’t know, but I think the rise of MediatR with ASP.Net MVC is a pretty damning statement on ASP.Net MVC itself. My thought is that Alba makes it so easy to test directly against the HTTP endpoints that we don’t need an extra layer. If we *do* turn out to want that, I’d pull in my own Jasper project instead (http://jasperfx.github.io/documentation/mediator/).

      As for feature folders, it’s really just putting things you develop together and likely change together in the same damn place. And I tend to prefer to write the DTOs right inside the same file as the controller at first, then maybe move them out later with any number of R# tricks. Having to hunt around for the right place to stick things across different namespaces or even different projects is killer friction to me.

  4. Hi, I’m a Redux maintainer.

    I’d encourage you to use our new official Redux Toolkit package. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire “slices” of state at once:

    https://redux-toolkit.js.org

    Note that we now specifically recommend _not_ using Immutable.js:

    https://redux.js.org/style-guide/style-guide#use-plain-javascript-objects-for-state

    Also, from my own experience, I’d say that TypeScript is definitely worth the effort. I wrote my thoughts on using TS here:

    https://blog.isquaredsoftware.com/2019/11/blogged-answers-learning-and-using-typescript/

  5. Hi, I’m a Redux maintainer.

    I’d encourage you to use our new official Redux Toolkit package. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire “slices” of state at once:

    https://redux-toolkit.js.org

    Note that we now specifically recommend _not_ using Immutable.js:

    https://redux.js.org/style-guide/style-guide#use-plain-javascript-objects-for-state

    Also, from my own experience, I’d say that TypeScript is definitely worth the effort. I wrote my thoughts on using TS here:

    https://blog.isquaredsoftware.com/2019/11/blogged-answers-learning-and-using-typescript/

    And finally, I’d suggest taking a look at Cypress.io instead of Selenium:

    https://cypress.io

      1. I had Babel.js set up to do React and TypeScript transformations literally within minutes with Parcel.js because it’s all in the box. So, yeah, not gonna do that.

    1. The application itself is .Net, so using Cypress.io isn’t particularly an advantage here. Any test that would be driving the browser would also be driving the system itself, so having .Net code to set up the data, then Cypress to drive the UI, then back to .Net code to check any changes to the system just isn’t what I’d prefer to be doing..

  6. “Probably with MVC Core for API endpoints, but Iā€™m voting to use it in the lightest possible way”

    Do you need serverside stuff, like state ? If not, just go with some Azure Functions, separated by concern/domain into micro function apps with some CRUD style endpoints/function app. Done it a few times now and its extremely light weight and rapid, both for dev and CI/CD. Oh, and VS Code all the way. So Mac is a nice tool

  7. I know this is an old post but want to ask you why don’t you like Angular? After working with Angular for several years, I would say that I also don’t like it. But I’m curious to hear your opinion.

Leave a reply to rc_blog Cancel reply