BrandGhost

How To Build Modular ASP NET Core Applications With Plugins

Want to build flexible and extensible ASP NET Core applications? Look no further than building with a plugin architecture! Plugin-based systems allow us to develop software in very dedicated modules -- even supporting the notion of vertical slices if you like the idea of vertical slice architecture. In this video, I'll walk you through how I setup ASP NET Core applications to be extended via plugins. Of course, there are pros and cons to everything... and we'll go over some of the complexity challenges of plugin systems in this video as well.
View Transcript
there are many different architectural patterns that we can follow in building software applications but in particular one of my favorites is the plug-in style architecture hi my name is Nick centino and I'm a principal software engineering manager at Microsoft in this video I'm going to walk you through building a plug-in style architecture in asp.net core it's building on some previous videos I have which I will link in this one as well so you can click above here and then go check that out if you haven't seen it already but it's going to be looking at leveraging autofac to do dependent injection with a little bit of reflection and assembly scanning to pull it all together so if that sounds a little bit mysterious you're going to want to check those videos out first then come right back here and continue on a quick reminder that if you like this kind of content subscribe to the channel and check that pin comment for my courses on dome drain with that said let's jump over to visual studio and continue on where the previous videos left off so a little primer on what the previous video showed was I have this solution file that has a couple of different projects in it I've gone ahead and I've added a couple more which I'll explain in just a moment but I was really comparing three different ways that we can leverage autofac and try to get a plug-in system pulled together for dependency injection so the very ENT scrap that sorry so the entry point to our program is just a single line and then I continued on from there to basically explain how we have this skeleton program that really just focuses on being able to scan assemblies get type information and then set up some plugins that do all the rest of the work for us so this core application that we have and in this case if we go to the solution explore on the left it's just called Full resolve web API and this is because it was the variation that could do full resolution of all of the types that I was interested in so these other two that's a problematic minimal API and service provider web API these are the two other variants that we're not going to look at because this third one pulls them all together if you've gone ahead and watched the previous video we got to a point where if you check out the plugins folder here I have a couple of plugins I've gone ahead and added one more and I've also reduced the one plugin that had a like a hello route on it um this was had a couple of other dependencies just to kind of illustrate how we can pull some extra stuff in from the container Builder we don't really need them because the previous video showed that we can go access them I don't need them for a simple hello route so not a big deal right now if we look at our application we have two plugins that I have inside of the same project which is a little weird I'm going to touch on this in just a moment if I check out the container Builder as well one thing that I've changed from the original project is that instead of having this code here which just looked at the current executing assembly which would work totally fine when our plugins are in the same assembly I've gone ahead and I've added this assembly scanning so if we read through this code that's on line 17 through 21 what I'm doing is checking in the running directory and this is just a a naive approach for doing this we can just go check for any dll file right and then from there I'm going to load in those assemblies and then put them in an array and then tell autofac to go register all of the modules from those assemblies now in your particular case you might want to have more specific filtering maybe your plugins are in a dedicated folder however you want to go structure that type of thing but this is how I'm doing assembly scanning for this example and really what I'm trying to call out here is that instead of looking at just one assembly the current one we're now able to go load dependencies from other assemblies as well that is going to include the current running program okay so the current status of the program is that we can go check other assemblies but we still have our plugins located inside of this current assembly so if we were to go run this right now if I press the play button what we should get is a web application that starts up of course on a different screen so let me go ahead and pull this over boom right we can see that it went to the hello route and if I test out goodbye we also get the goodbye road so that works totally fine nothing too fancy there but just to explain how that's working is that I have these two modules which are an autofac concept and then I am asking in that module for the web application and then just using a minimal API if I look at the hello plugin it's the exact same except the road is is a little bit different but very simple right these two I'm going to use airor quotes here cuz they're plugins but they're still within the core application when we talk about plug-in architectures you can technically do this right it's not wrong and in your really trivial cases right if you start pulling stuff out into other projects that's more of a plug-in approach but the more that you start to do that this is where the complexity is going to keep adding up more and more and more so if you just wanted to start isolating things and you don't really have a need to pull them into other assemblies this could be a great approach just to kind of get like a what you might call like a vertical slice right you can go put your functionality into a module maybe you want to build out a folder structure some name spaces in your project and you can treat each of those kind of like its own you know its own vertical slice or own module or plug-in right so you could go do that but if you want to start being able to say grow your team and you want to grow the the software that you're building across multiple teams uh multiple developers contributing into this when you have plugins this does afford you the opportunity to start having more isolated contributions which can be really nice there are situations of course where you want to open up third-party plug-in support as I walk through this particular example the plugins that we're talking about here are going to be more focused on your internal developers even for myself when I'm building my own projects I like leveraging plugins because they force some isolation and for me that's really nice because I don't have things bleeding across different boundaries where it might not make sense if I find that I'm starting to do that right it makes me question hey did I set these things up properly maybe I came up with a an odd plug-in boundary that doesn't really make sense and it's a good sign for me like maybe I need to rethink about this design so it's a bit of a forcing function for me personal preference when I'm building things but this can be really nice as your team starts to grow and you have some complexity that could be introduced into your application and now just a quick word from this video sponsor which is packed publishing P has lots of awesome CP and net development books and in particular I wanted to talk about this one called apps and services with net 8 I have a copy of the book right here it's by Mark Price it's an awesome read you can learn about Blazer Maui grpc graphql and many other things as well so I highly recommend giving it a check you can check it out in the links Below in the description and the comments thanks and now back to the video so we're going to switch gears here a little bit and I was mentioning that when we have these plugins you know these modules inside of our main application this could be just good enough for you as a solo Dev or a small team where you don't really need the overhead of pulling things out into different assemblies that's totally fine but if you're trying to go a more like true plug-in based approach technically these things would exist in different assemblies and this is where we can start to really add a lot of complexity so I want to walk through a couple of scenarios one I'll kind of explain high level and the other one we're actually going to do in the code and try exercising this so the idea is that when we have the modules as plugins inside of our code right now in the main project what's happening is that code is compiled into our our dll in this case or assembly that compiled code is just part of it right it's in the main assembly generally with plugins the idea is that you can dynamically load these things at runtime that's a side effect you might want this in some cases where you have some application where you can literally as the program's running uh you can sort of uh do a refresh you can rescan for plugins and load in new functionality at runtime which is super cool but a lot of the time especially for asp.net core stuff I find we don't really need this concept of like hot reloading right so having this dynamic plug-in loading ability is truly it is a little bit of overhead but what we could think about is consider if you're building software and you have a large team or you have multiple teams that are contributing functionality to this software in a situation like that what happens is the more people you have contributing to a single code base can get pretty complicated pretty fast something that you could consider is that if you have these teams working in these different functional are areas of your product so say it is an asp.net web service in this case if you broke things out into plugins they could be contributing to those areas specifically right they could own a full vertical if that's how your plugins are structured or they could be dabbling in just you know one plugin in general but the idea is that your build system could start taking on some of this complexity and here's what I mean by that right now because these two modules if we look at our solution Explorer because those in the main project when we build in Visual Studio like I said that code's going right into the assembly as soon as we start pulling this stuff out how do we make sure that when we want to go run this thing we're going to have the code in the bin folder this is where if you're if you have a build system right you might be able to have different build jobs running building different plugins they might not even all exist in the same solution right you can totally start pulling all of these pieces apart you could use different repositories you can kind of do anything you want but then your build system is going to have some type of thing where it needs to be able to grab the plug-in data and put it with the core application so you need to build some of that complexity somewhere else now that might mean for example your plugins are building you can do this in so many ways but you got to have a build job for each plug-in it's going to put that into a shared folder somewhere whatever that looks like and then the main build for your core application will go fetch the stuff from that directory and say hey all the plugins are here let me grab them when I go build you could do it with nougat packages so your plugins will publish to a nougat feed and then when your main application builds it pulls those nougats down right you could do it in many ways but like you can see that anything I just described there is far more complicated than just leaving the stuff directly in the the main application I like highlighting this because it's critical to understand that I think plugins can offer a lot of flexibility but truly if you're not used to using them and you just jump rate to you know having all these different projects like you the complication and complexity just goes through the roof so that's what I think is I don't want to say the ideal way but probably the most realistic way if you have many teams or you're kind of doing things at scale I think that there is a shortcut and I use this shortcut all the time for reference I worked on just giving you one example I worked with on team of roughly three or four people cuz it was different people at different times and we had a solution with over 200 projects in it that's a lot of projects but a lot of that is because there's plugins and stuff that dynamically get added in because we were just one team working in one spot it was so much overhead to go if we had to go make different build jobs for all that stuff so the cheat is being able to have Visual Studio do that for us so I'm going to walk through that as an example because I think that that can offer offer a lot of value if you want to get plug-in benefits structure your code some way and not add so much complexity so let's see it I'm going to go ahead and take the plugins that we have you'll notice in the solution exploring the left hand side I have a plug-in one and plug-in 2 project they're currently empty so I'm going to take the hello plug-in module I'm going to put it into plugin one I'm going take the goodbye one and put it into plugin two and then I'm going to delete this plugins folder right at a visual studio it's gone now and you can see that I have plug-in want and plug into here now what I'm going to do is run this and we'll see that when we go to run this web application now going to the hello route doesn't work right and the same idea if I go to goodbye also does not work and it doesn't work because Visual Studio doesn't know that we care about these plugins I literally from the perspective of this project full resolve web API I just deleted that code it doesn't exist right it moved but this project doesn't know or care about plugin one and plugin two but if we go look this is it's the same code I just moved it right this is how you would go create those plugins and this even applies to the other example I gave earlier so if you wanted to go have a build system go build these separately and all that you could do this you'd have the same thing you need to move the code out of your main project right how do we get the shortcut here what's something that I've used and had a lot of success with and the reality is it's just a bit of a cheat because you're breaking a rule but Visual Studio is so good at doing this for us I'm going to show you it manually and what I mean by that is if I open up this in the folder Explorer here so really what we need to have is our plug-in one and plugin two inside of this bin directory that's the goal we need to have right so if I go back up a little bit let's see go up another folder so plugin one and plugin two right now I need those dlls and how do I get them right if I go build and I build this one too so both of those are built now so I go in a bin directory I take plugin one right it's it seems kind of silly and like you can see me I'm doing this manually which is why it seems so silly so I'm going to go into here drop it in if I go back go to plugin two now I'm just copying and pasting the stuff right almost there I'm trying to talk and show you at the same time that if I just go press play now I didn't touch any codee right like the hello route works the goodbye route it works I didn't have to change any code and we were basically able to add this functionality back in just by having the dlls present that's essentially what your build system would have to do in different ways whether it's Nate packages or whatever else so you would need that kind of functionality but we can make it easy for us and this is the shortcut I'm going to delete these again so you know that I'm not cheating with the shortcut I'm about to show you but if I go now and on the full resolve web API point that I want to get across here is that Visual Studio makes this so easy for us if we just add them as dependencies it completely defeats the idea of not being able to see this plug-in code while it's uh you know while you're in development because you want to keep it isolated if you're finding like I need to from my main application directly call into this plug-in code that plugin code probably shouldn't be where it is that's what I was giving you earlier when I said an example of like these boundaries this would be a case where I would say to myself hey if I need to directly call this plug-in code probably a smell in how I've designed things by having them added as dependencies technically it makes it a little bit dicey because someone could now go directly access some of this stuff there's one thing protecting us though if I go look at the modules themselves this internal keyword is really what's keeping us protected here if I go into program now right of my application technically I can't do hello plug-in module can't be seen in visual studio right it it's unable to go resolve it and it can't because it's internal even though I have a dependency on that plugin so that's a bit of protection however if some people were you know not using internal and they are using public uh public instead now if I go here Visual Studio can find it this is the thing that we don't want to have happen one more nice guard rail is that the more of a skeleton application your core app is the less likely it is that you're going to find yourself trying to do this kind of thing because you should be ideally you know in a perfect world able to add your functionality in through plugins you shouldn't have to come into the core app to do this but I did want to highlight that this shortcut basically allows you to break some rules if you're not paying attention this is why it's it's worked for me up to this point because like I'm making the rules and I'm aware of what's going on and I'm just using this as a shortcut because I didn't want to spend the time making more complex build systems I still have not done any magic for copying files but if I just go build this now that's all that I do is just build it you can see plug-in one and plugin two are directly in the bin folder not only that we get the pdbs automatically for debugging like it just works it's super nice to have and it avoids you going especially when you're debugging in your visual studio and uh your Dev environment it avoids this issue of like oh crap did I forget to to rebuild oh crap I rebuilt but did I forget to copy it it just happens and it's so nice because now we can just go run things and again like because those files are copied there it just works right away that's a bit of a cheat code I like to use but again if I were growing teams or growing this product surface area eventually I would like to get to the point where I'm saying hey you know what this might not be a scalable thing we want to have people we might want to pull plugins from different repositories and all this other kind of stuff we probably want I would probably opt for something like nougat packages to be able to pull that stuff down so you'd have some type of manifest you could Define and that way you have a central manifest that you can say hey these are the plugins that my application cares about pull them in dynamically during the build and you're good to go I think the cheat code's just helpful when you're trying to get up and running or you're a very small Dev shop like I'm doing in my own case that is my Approach for building plugin systems in asp.net core I hope you found that helpful and I do want to be able to show you how this can work in a Blazer application so if you're interested in that when that video is ready you can check it out here thanks and I'll see you next time

Frequently Asked Questions

What is the main focus of this video on building ASP.NET Core applications?

In this video, I'm focusing on building a plug-in style architecture in ASP.NET Core using Autofac for dependency injection, along with reflection and assembly scanning to manage plugins.

Why should I consider using a plug-in architecture for my applications?

I recommend using a plug-in architecture because it allows for better isolation of functionality, which can be beneficial as your team grows and the complexity of the application increases. It helps prevent code from bleeding across different boundaries.

What are some challenges I might face when implementing a plug-in architecture?

One challenge is the added complexity of managing multiple assemblies and ensuring that your build system can handle them correctly. If not managed well, it can lead to complications, especially when scaling with larger teams.

These FAQs were generated by AI from the video transcript.
An error has occurred. This application may no longer respond until reloaded. Reload