Refactoring Blazor UI Plugin Code And How We Can Use Vertical Slices
September 13, 2023
• 1,261 views
How do vertical slices, plugins, and ASP.NET Core Blazor all fit together? Beautifully, if you ask me! Check out this video where we refactor some Blazor plugin code and explore how vertical slices tie into this.
Have you subscribed to my weekly newsletter yet? A 5-minute read every weekend, right to your inbox, so you can start your weekend learning off strong:
https://www.devleader.ca/newsletter
Check out more Dev Leader content (including full in-depth articles with source code examples) he...
View Transcript
so I had a pretty embarrassing experience and that's when I was making the last video of Blazer plugins I actually caught myself while I was recording the video demonstrating the perfect use case for plugins and here I was showing everyone here's how you use plugins but not listening to my own advice I want it to be transparent too so I actually included that segment in the last video and I'll link that above for you so you can watch that first if you haven't seen it yet and then come watch this one because we're going to build upon the topics that we covered there so the use case that I want to look at for plugins and Blazer today is actually refactoring the sample application where we were adding all of the other plugins into having the navigation actually be plug-in driven and while I show
you the implementation for that along with source code that'll be up on GitHub I want to talk about vertical slices as we go through this as well because I think it's the perfect opportunity where I'm actually demonstrating a use case for both plugins and vertical slices and how they work together so by the end of this video you should be able to see how we can change the navigation system in the sample Blazer application to be plug-in driven I'll explain vertical slices and how they tie into that and we're actually going to have plugins that have plugins as well so it's going to be a little bit Wild and the other thing that I want to call out is that as we go through this code it's not totally done but when I have it pushed up to GitHub for you to go check out
I will try to make sure that all of the other plugins are migrated so only one of them supports the new format and the other two I'll make sure to do when it's pushed up to GitHub okay I get it enough blabbing Nick let's go check out the code so in the previous video I had three types of plugins and I actually only moved one of them out and what I've done is I move that into a folder that's called features and the idea behind having a folder like features and then having the other things split out into it is just an example for how to organize some stuff I wouldn't necessarily recommend that you have to follow this pattern or anything like that I just wanted to move out one plugin and have it separated so as I made this video you could see
the different code that I was touching I'll absolutely leave that as an exercise for you right now to figure out how you'd like to organize your plugin code and in future videos and blog articles I can actually touch on that for some best practices that I found so I decided to move out the plugin type that was generating HTML from the plugins and then we would wrap that into fragments so the plugins themselves would only be responsible for generating HTML the core app at the time was responsible for wrapping that HTML into a render fragment but what I realized was I was creating these demo plugins is that I should have been following a vertical slice approach and if you're not familiar with vertical slices the ideas that we're focusing on delivering a feature that might span through multiple layers on an architecture so that
would mean that your feature might have some user interface code it might have some business logic kind of in the middle and then some database access at the back all of these pieces would need to be touched in order to deliver a vertical slice now you can organize your code in a lot of different ways that's modular and flexible and actually makes doing vertical slices a lot easier and if you think about plugins the fact that I've actually separated out this code that I have highlighted in the solution Explorer into a separate folder this is basically moving apart parts of the application to be modular and separate from the core application now we already did that with the original plugins but like I said what I noticed when I was making the last video was that the navigation part itself and the fact that I
have all of these different demos showing different types of plugins actually would lend itself well to Vertical slices so this is where it's going to get a little bit weird but technically all of the different plugins I was showing you before and the fact that I want them to show up as different Navigation items the types of those plugins are actually plug-ins themselves all right so if your mind just got blown from that the idea is that the navigation menu that we had on the left side of the application that's actually going to be plug-in base now and the idea is that those plugins are going to be the types of plug-in demos that we want so bear with me for a second because what I was showing you before with having HTML being generated from a plug-in and then render fragments generated from plugins
the idea was that on those screens you could have multiple plugins that would give you that content right so you have a list on that screen However the fact that we can even see that screen that's a plug-in type and that plug-in type for demo purposes actually should have been pluggable in the first place into the navigation menu so it's a little bit confusing but for each plug-in type we can have a list of plugins for that so it's a double layer of plugins which is a bit of a New Concept compared to last time so I just wanted to call that out because we might see some similar code and you might be thinking I thought we already had you know a class for this or I thought we registered this already but you have to keep in mind that the plug-in type itself
for the type of demo that we want to show that's going to be top level plugin for the navigation menu and then each of those plug-in types can have plugins to show in the list on that page I'll probably repeat this a couple of times for the video because I don't want it to be confusing but that's the first call out for it so let's go back to visual studio okay so hopefully that wasn't too confusing but let's look at how we actually pulled out a plug-in type as a top level plugin and then the plugins that have to go into it so what I did was I made a top level plugin called HTML fragment plugin feature and if we look at that it's going to have the page right so the one page that we had that was actually showing the plugins that
were pulling out the fragments so this HTML fragment plugin actually has the one page that we had from the previous example and I just renamed it I think it was called Pages two or something like that really generic and this page was actually responsible for pulling the HTML off of plugins and then wrapping it in this render fragment so I've just moved that page over to this plugin because this plug-in type is actually going to be responsible for that and then I have another class here which is going to need some explanation it's called HTML fragment navigation plugin and you'll see that it has this array of navigation items and we haven't talked about what a navigation item is so we'll touch on that in just a moment and then we have the plug-in module itself and the plugin module is only going to have
one of these generic plug-in providers and it's going to have ihtml fragment plugin as the type so this plug-in module if you think about the core application that we had before this plug-in module is actually going to be able to register the plugin provider specifically used for this plug-in type and I'll repeat myself because I know it's a little bit weird but the only spot that's going to be using these plugins I HTML fragment plugin is actually this plugin itself and then the plugins that need to implement ihtml fragment plugin so we're currently inside of a plugin but it's also able to load these other plugins and this other plugin we only have one example of that and I'm just going to show you what that one looks like because it's the exact same one that we had in the original project and that's right
here if you watch the other video then you'll know that I had this HTML and an alert and then this HTML actually got wrapped inside of a render fragment in the core application but we've kind of moved things out one layer so that wrapping doesn't happen in the core application it happens right where I showed you on this new HTML fragment page which is just the original page moved over to this other plugin so I know it's a little confusing because we're two layers of plugins in and it's worth pausing because when we talk about vertical slices and building plug-in architectures and things like that it's really important to make sure that you have clear naming that you have clear conventions and that you have good code organization currently I have really terrible naming in this project and I have really terrible code organization as
well so it's not making this video any easier to create and explain things unfortunately but I still wanted to walk through this code because I thought it was useful in the original code we had this plug-in API and if you recall from watching the first one I had I plug-in API I plug in API 2 which is now gone and I plug an API 3 and I mentioned in the other video that I was just being lazy dropping them all into this single file but I needed to be able to move that plug-in API 2 out I actually gave it a new name and I called it ihtml fragment plugin and what I've done is I've moved that into yet another project and some of you looking at this are already going Nick this is way too many projects for what you're trying to do
and I totally get it because with this little bit of code and we only have basically one plug-in for everything it's certainly Overkill but the whole point of putting this together is to show you that we can easily extend it so if you were architecting something and you had no intentions of actually making pieces of it extensible it would absolutely be Overkill to go make everything plugins but this was a special case and because it's a plug-in demo about building plugins and it's plugins all the way down I was going to make this highly pluggable so this plug-in API project only has one single interface in it and it's I HTML fragment plugin and that way the actual plug-in implementation and this is going to be the one that shows up in the list that we just looked at here so this HTML will show
up in the list and it needs to reference that ihtml fragment plugin the top level plugin which will actually have the navigation menu item is going to have this plugin provider that I showed you that's going to pull all of these back out and this example that I'm using if I jump again to this other module sorry for going back and forth I left a comment in here that said we're not using the reflection example that's going to pull that code out automatically it's not going to register each of the types automatically so we need a dedicated module my intention is that when I move the other plugins over to this format I'll show the example of using reflection there just like we had in the other video but for this case I'm not actually going to do that and just wanted to leave it
again for some Legacy purposes so you can compare and contrast what you might want so the final thing I want to touch on in this particular plugin is just going back to the plugin itself so I mentioned that the plugin itself has these nav navigation items and we just made an array with one navigation item in them and I wanted to talk about this for a second because it's a point that was brought up in the last video alright so in the last video I was showing an example where we could have HTML generated from the plugins themselves or we could have render fragments come out and what I tried to demonstrate there was that the code was very similar we just moved some things around and made the apis different for the plugins but the point that I was trying to make was that
when you're designing applications and you want to have plugins you need to give consideration for the API that you want to have and this does take some thought because you need to think about who is responsible for what so in that example I was showing that you could have HTML like raw HTML come read out of the plugin and it could be whatever and we just hope it works because you trust the plugin we could have render fragments come back out so you're pretty confident that it's going to all work but what's actually displayed inside of that render fragment who knows and when I was thinking about the navigation menu I wanted to come up with a good API contract that I was happy with and I made the decision that the core application itself would be responsible for basically theming styling and controlling what
that navigation menu looks like and all that I want is some properties that come out of the plugins to control navigation so all that I needed was a title so that'll be the text that we see on the navigation item and then I need basically a link to where we're going and that way the plugin itself can give a link to a page that it has and the plugins are not responsible for creating that menu item they're just responsible for providing some properties that we'll use to create that item ourselves so to rehash where we're at I just explained that we have navigation menu items that are populated from plugins those plugins are only going to return a title and a link for when you click that navigation item so that means the responsibility of the core application is building that menu and the responsibility
of the plugin is just giving a title and a link so really simple from the plug-in perspective and that way when we click that link and we load up that navigation plugin to bring us to a page then that plugin can do whatever it wants it just so happens that in this whole project and all the demos that we have going on here each of those pages that we're interested in also has plugins and that's where it gets confusing right so we're going to click this page it's going to list all of the HTML fragment plugins there's only one of them and that's why we have two layers of plugins so hopefully you're with me so far I know the double layers of plugins is kind of confusing but let's go back to visual studio and see how this is actually consumed in the core
application all right if we're looking at the core application here under Blazer plug-in example if you look at Pages you can see that I pulled plugins to Razer out of there and that's actually what ended up here so I already showed you that I just wanted to demonstrate that yes it is in fact gone and what we're going to do is we're going to look at the auto fact module and I've just given it a different name it used to just be called I think uh Auto fact module something really generic but we're actually just going to call this a navigation module now because this module is dedicated towards navigation so I had mentioned earlier that when you're trying to deal with vertical slices code organization is really important if we're using something like autofack it lets us group code into modules and the real
benefit there is just that we can give it a name that we're familiar with that we can find and navigate easily and then we can organize the code that we want to register for the different types so really it's all about code organization in this case so the core application now only has this single module and it's responsible for loading up the navigation plugins so you'll see again we have another generic plugin provider and it's only loading in I navigation plugins so this plugin provider will return all of these types and this is that reflection example that I had mentioned in the other video where we're going to go look for all of the plugin dlls and try to pull out all of the navigation plugins from those dlls so this code here that's doing that reflection and automatic Auto fact registration I mentioned I'm
going to jump back to this other module I showed you right at the beginning that could have been used here it could have been used on the page that's actually going to show the list of HTML fragment plugins and in fact I will make it work that way for the other ones but this code here is just registering a single plugin because we know about it but this code here in the core application is actually scanning all of the dlls looking for this type specifically so the point of that is just to illustrate that they're serving the same purpose it's just a different way of doing it and this one is a lot more extensible but you do have to think about the right filtering and error handling that you want to support So once we have this generic plugin provider actually able to pull
in these I navigation plugins where do we use them well of course in the nav menu so if we go to the nav menu we can actually see that this code has changed a little bit and what used to happen is in this part of the menu we would have basically a list of hard-coded menu items and in the last video I caught myself copying and pasting the third one in and usually the this kind of thing happens on the third or fourth example of you know copying and pasting something where you go wait a second I think I've seen this before if it's just doing it you know one copy paste like it might not be enough of a pattern but generally the third or fourth attempt is where something clicks and it was number three for me and I realized okay let's try
to make this a little bit more extensible so I just have a pattern for if the items aren't set we'll say that it's loading if we have no plugins so the count is less than one we'll just show that in the in the menu itself so that you know something's going on and then if we have items in there we'll actually populate them and if you recall I was saying that we only need two properties off of the navigation items that come from the plugins one of them is the link right so when you click it where are we going and the other part is the title so really simple those are the only things that the navigation plugins can provide if we scroll down a little bit we can just see how we're actually pulling that stuff in it's the same as the other
videos so we're just asking the plugin provider to get the plugins and then pulling all of the data transfer objects the dtos right that are just the navigation item so this class here and they only have title and Link on them so it's super simple and it's just like the other examples we saw in the last video and that brings us to one more point that I want to make so again when I was creating this video I ran into a little bit of a problem and that's because I'm actually not super well versed in Blazer enough to know about having pages and different assemblies and this caught me off guard it's the reason why I've only migrated one of the plugins at the time of recording this and like I mentioned I'll do the other two after but it had to do with being
able to resolve pages from other assemblies so I'll show you the code in just a moment but something that I don't like about this code that I wanted to explain first is a lot of the examples online basically said to make this work we need to have all of the assemblies listed out that we want to load razor pages from now the problem is that because we're using plugins we want this to feel more Dynamic so I created a way that actually is a little bit more Dynamic it's not exactly what I wanted and it's not currently refactored to only run once so every time this code is loaded it's going to try and load the assemblies from the dlls and with the break point there I've noticed it's happening a little bit too frequently I could do something like you know a run once
with a lock or something on it but I want to do a follow-up video that shows Dynamic loading as well so I figured I would leave it for now it's it's a little bit inefficient I probably would not do this in production but I'll call it out now and when we go back to visual studio I'll kind of show you how that's working but the idea is that when I went to go run this for the first time and had all of my plugins configured properly so the navigation items would show up when I clicked the nav item it was telling me that it could not find the page and that page was supposed to come from that dll so it's just because the router in Blazer is unaware that it needs to go get pages from those assemblies I could not find a way
to do this in code yet so it's kind of in the the Razer page itself let's go check it out and you'll see what I mean all right so I've just opened up app.razer and the code that we're going to look at is on this router so you can see on line 8 here I need to have this additional assemblies provided and additional assemblies is actually going to be a property that we can access down below here and it's going to have this navigation plug-in provider and what is that well because we're in the core application we have generic plugin provider with type I navigation plugin inside of it we're injecting that into here and then we can ask to get all of the plugins out of there and what I'm doing is for each plugin I'm asking to get the type and then the
assembly where that's defined so this isn't Stellar I'm not a huge fan of this but the idea that I figured would make this acceptable is that in this application we could consider that only the navigation plugins are allowed to provide us new pages that we can navigate to now if you were building a real application that assumption might totally break down because if you were on one page wanted to click something that would bring you to another page but wasn't necessarily using the navigation menu this entire assumption would fall apart however in my particular case my navigation items are different types of demos for plugins and each of those plugins is just a list of other plugins so because I have no navigation happening outside of the nav menu this felt okay to me but I wanted to call it out because if you're watching
this video looking at this code and you want to go copy it this is not something I would recommend doing and then the other part that I mentioned is that this actually gets called multiple times so we would want to do something like cache it or come up with some other logic to minimize how often this runs but another thing that I called out is that in a follow-up video I do want to show dynamically dropping in plugins at runtime and having things refresh so I said for now let's live with the inefficiency so just a quick recap we do need these additional assemblies provided to the router and as soon as we have this that allows the application itself to actually know to look for razor Pages inside of the plugins and that means that this page here HTML fragment page that's inside of
our plugin can actually be navigated to now when we go to this route at the top without the code that I just showed you when we navigate here it says that it can't find it so the part that I would like to clean up is actually doing this in code behind and not actually as part of this uh this Razer page it feels a little bit gross to me and I'd love to actually find a way to access the router and deal with this additional assemblies behind the scenes and not right here but that's for another video and another problem them to solve later okay so the Moment of Truth we all want to see how the navigation menu can get populated now so if you recall in the previous video I mentioned that with our plugins we do need to build them and drop
the dlls into the bin folder so if you don't do that and drop the plug-in dlls into the core applications bin folder that core application will never know about those plugins a bit of a hack that I do in my own projects is I actually add them as dependencies to the core application and that way anytime I'm building in Visual Studio they'll get copied into the bin directory automatically if I press clean it'll clean up that bin folder it works exactly as I'd expect the reason it's a hack is that the core application should not have to know about those plugins in the first place it almost defeats the point of having the plugins and it means that if anyone's coding in the core application they can actually see that plug-in code and start referencing it directly so I don't recommend doing that but I
have a lot of use cases where I know that that will work and I know that I'm not going to abuse it so it ends up working really well for being clean in visual studio so I'll go ahead build these drop the dlls into the bin folder for Blazer plug-in example and that means that I need to take this dll here right because this project is going to be the plug-in for the navigation item and then this dll as well because this is actually going to be the one list item that shows up on this page HTML fragments page so I'll do that right now a quick little edit and we'll get you right into the app all right so we have our app up on screen you can see already that the left nav is doing what we expect and that's because I mentioned
the other two plugins are not migrated we only have this one that we just spent all that time walking through so that one plug-in at the top level for a navigation item and was only able to provide a title and a link so this is the title for the menu item and if I click this this is going to take us to the link that that plugin provides so if you see the address changed to be the URL from the plugin so HTML fragments page and we're able to see this page because of that router stuff that I was just showing you and then this page itself also has plugins and it only has the one which was copied from the original demonstration that I was showing in the other video and now that we have a visual hopefully I can bring it all together
for you and show you the multi layers of plugins right so we can keep adding plugins here that are of type ihtml fragment plugin so the more of those types of plugins you add the more list items show up in this space here and if we want more top level plugins so other types of demos we would add them as navigation plugins and we would get more menu items so in my case I have two others to migrate so I'm going to have two more menu items and each of those actually only had one plugin as well because they also have lists so that means I will have one two navigation items so two more and then for each of those I will have one plug-in each so in total that's four more plugins I have to create now wrapping it up with vertical slices
because we have a visual the cool thing is if I said okay next vertical slice I'm gonna go migrate plug in one type that means that I could go create a plug-in for that first navigation item and land that and when I click that it would actually bring me to a screen that says hey there's no plugins here but that's part of that deliverable and I could make that right away it would be one plug-in to go land and then from there the follow-up would be okay how do I move that other plug-in out so that when I click that new nav item we get one item showing up in the list just like this one right so that's two individual deliverables that I could have and then I could just repeat that process for the third plugin to get the navigation item here so
that one here another one that follows and then a third one and then the fourth deliverable that I could have is a vertical slice is again migrating that last plugin so that when I'm on that new page we get one more item to create this video I actually did kind of like two vertical slices together and that's because I got this HTML fragments list item showing up here and I got this plugin showing here all right so I realized that was probably a whirlwind of code to go through and a little bit confusing with that multi-tier plug-in system going on and it doesn't help that I don't have really good naming conventions in this demo project right now but here's what I would recommend when this code is available on GitHub I highly recommend that you pull it down and you play with it so
that you can adjust the things that you're looking at and see how the changes take place one of the reasons that I'm leaving leaving a couple of different patterns in that code is so you can see how they work and then you can decide for yourself what you like better and where it might be more applicable so one of those examples was showing you that plugins had to have their own Auto fact modules to register themselves that gives the plugin the responsibility of registration personally I don't really like that but you might and that's okay so the approach I like is the reflection approach where I would go scan plug-in dlls and then actually register the plug-in types from those dlls sort of at the parent level to that plug-in you might not like that because maybe the filtering is too complicated or you don't
want to handle the errors whatever it happens to be but that's how I like to do it another takeaway here is that we can have plugins within plugins so makes it a little bit confusing and that's important to call out because if you're going with plug-in architectures and if you're someone like me who really likes using them you do have to consider that when these things aren't clear it can make make it very confusing for other people to follow so the more that we have things separated and modular the more important it is to have good naming conventions have good code organization and overall General structure that's going to make sense to other people and I hope you believe me because as you are watching this video you probably felt like a couple of times like this is a little bit confusing I can't really
follow why Nick has put the code in these spots so when it's pushed up to GitHub I'll try to make it a little bit more clear but there's just a perfect example of why you really need to pay attention to this kind of stuff the final takeaway that I want you to have from this video is the fact that we can use plug-in systems to have vertical slices be delivered more easily to deliver vertical slices so delivering a feature end to end you don't need a plug-in system there's nothing that says you have to however to make delivering features like that easier you do want to have modular code and for me plug-in systems are the epitome of being able to do that I fully acknowledge that it's not the only way it's just something that I've adopted that I like to use in all
my applications so thanks for watching if you found it a little bit confusing try re-watching some parts and again I highly recommend you go check out that code on GitHub play around with it because I think the best way for you to understand this is actually to tweak the variables and see how they work on your computer thanks for watching and we'll see you next time
Frequently Asked Questions
What are vertical slices in the context of Blazor plugins?
Vertical slices focus on delivering a feature that spans multiple layers of an architecture, meaning that a feature might include user interface code, business logic, and database access all together. This approach helps in organizing code in a modular and flexible way.
How do I organize my plugin code effectively?
I recommend creating a folder structure that separates different types of plugins, like moving them into a 'features' folder. This is just one way to organize your code, and I encourage you to find a structure that works best for you.
Why is it important to have clear naming conventions and code organization in plugin architectures?
Clear naming conventions and good code organization are crucial because they help make the code more understandable for others. When working with complex systems like plugins within plugins, having a well-structured codebase reduces confusion and makes it easier to maintain.
These FAQs were generated by AI from the video transcript.