One of the things I like about Angular is that you don’t need to make as many of these choices. If you use Angular, you don’t have to spend hours searching NPM and Github for the different pieces of your web app. There are still choices to make though. In this post, we’re going to focus on how to integrate your front end (Angular) with your back-end (ASP.NET Core).
We are going look at three ways to build a web application. We’ll begin with the time tested monolith. Then we’ll move onto the shiny new “serverless” architecture style. After that, we’ll explore microservices architecture. After this post, you’ll have a good grasp of each of these styles and know when to use each one for your own applications.
If you’ve been building web applications for any amount of time, you’ve probably worked on at least one monolith. In a monolith, the entire application’s workflow is in one code base. In the .NET world, that means everything can fit into one big solution file. Monolith doesn’t mean we can’t have a reasonable separation of code. You can create module boundaries and separate your code into projects. You can even implement an n-teir architecture and separate your application into layers. The key to the monolith is that the whole process is in one self-contained code base.
Monolith architecture gets bashed on, but it’s the default for a reason. There’s several advantages to the monolith style. For one, everything is in one place. If you need to refactor, you don’t have to worry about moving across service boundaries. Tools like ReSharper make this even easier. Monoliths are also easy to deploy. If you are building a small or medium sized application, monolith architecture is a good choice.
While monoliths are fine much of the time, there are situations where they tend to fall down. The first problem with monoliths is that they are hard to scale. If you have one piece of an application that’s experiencing a heavier load, you still have to scale out your whole application. Monoliths also deter code reuse. In many corporate environments, there are resources that are used by more than one application. With a monolith, you end up copying the code to access those resources. Because there’s so much code in one place, larger monoliths can be hard to understand. This can make it harder to on-board new team members. Once your application gets to a certain size, you should peel microservices off of your monolith.
There are a few ways to implement a monolithic Angular application. The easiest way is to use the built in template. Visual Studio comes with an Angular template that includes some nice features. I prefer to use the Angular CLI, so I don’t use the included template. If you also want to use the Angular CLI, I have a post on how to use Angular CLI with a .NET Core project.
Angular CLI with .NET Core
As I learn more about Angular, I’m beginning to favor another approach. The truth is that .NET doesn’t add much to the Angular front end party. Given a clean slate, I’d run the Angular half of the code as it’s own project and use .NET Core for the API. Angular CLI is perfectly happy self hosting or you can deploy the static assets to an IIS site.
The serverless architecture is the most recent pattern on the list. It’s also the worst named pattern on the list. The servers [obviously] exist, but you don’t have to care about them. It’s “serverless” to you.
Serverless architectures rely on services that host and run your code for you. Instead of managing VMs or physical servers, the service abstracts that away. If 500,000 users come knocking on the door to your site, you don’t even need to push up a slider bar. The service scales for you.
When people think of serverless, they’re usually thinking about Functions as a Service (FAAS). FAAS platforms allow you to host small bits of code in the cloud. This means a single endpoint. FAAS is not the only way to be serverless though. Services like Firebase can encapsulate the your whole back-end.
To integrate this with Angular, you compile your Angular application into a static web site. Then you call the APIs you create in whatever serverless service you’re using. This post has details on how to build a serverless Angular application using Azure Functions and an Angular site hosted in an Azure web site. You should be able to adapt these instructions for your favorite cloud provider.
Serverless isn’t for every situation, but there’s some definite advantages. The biggest advantage is that you can quickly get your code into production. You don’t need to configure servers or set anything up. You just drop your code into the cloud and go. Server level concerns like scalability and server management are gone. Serverless architectures are also very cheap to start with. FAAS platforms charge you by usage, so low traffic apps are dirt cheap. Static websites are also fast and cheap. Since most static assets are cached, you can serve a ton of users with very little impact.
There are a few problems with serverless. The big one for me is complexity. Once you have a certain number of functions, they become hard to manage. I’d rather have a web service or series of web services over a suite of cloud functions. Functions also have somewhat unpredictable pricing. A misconfiguration in your service or a bug can leave you with a huge cloud bill. The serverless pattern really shines when building prototypes and minimum viable products (MVP). You can get code into production and start iterating. Later on, you can refactor your functions into services if your application takes off.
The microservices architectural pattern addresses some of the issues found on larger products. Microservices divide the functionality into tightly scoped services. These services are loosely coupled, so you can work on them independently. You can even build them using separate technology stacks.
Microservices architecture has several advantages. First, it’s easy to scale microservices. If you have one part of your application that’s receiving lots of traffic, you can scale only that part. Microservices are also easy to understand. Because they are small, it’s easy to drop into a new service and find your way around. Each service is easy to reason about. This is my favorite feature of the microservies architecture. Getting up to speed is much easier than on a big monolith application. Microservices also encourage reuse. In enterprise environments, you often have several applications hitting the same data. With microservices, you can make one service that serves many applications.
While microservices are great, there are some drawbacks. The biggest drawback I’ve seen is performance. Because the services are loosely coupled, you use JSON as a commnication mechanism between services. This means you end up doing a lot of serialization and deserialization. Each service boundary you cross incurs an overhead cost. On larger requests, those costs are significant. These service boundaries also make it harder to troubleshoot problems. Tracking down which service in a chain of calls is breaking down can be frustrating.
Another pitfall of microservice development is deployment complexity. Even though services are loosely coupled, you still need to manage the dependencies between them when deploying larger features. If you don’t have a good continuous deployment and integration pipeline, you are going to have a bad time.
If you want to see a sample microservices and Angular application, I have one here.
Each of the architectures in this post shines in a particular set of circumstances. Monoliths are great for small to medium sized projects, but are not ideal for larger applications. Serverless architecture is great for small apps, but not for large enterprise applications. Microservices architecture works well for large applications, but isn’t worth the overhead on small ones. Figure out the application you want to build and choose wisely.