Introduction to plug-and-play architecture in Asp.NET 5

This is a part of a series of blog posts about design and architecture patterns.

Disclaimer!

This blog post will only cover the theory of what every software architecture needs to provide in order to be considered "useful" and reliable. More practical approaches will be shown in the next blog posts.


Introduction

Let us start with a simple premise that software solutions usually obey:

Building scalable and reliable solutions requires an architecture that solves all cross-cutting issues that can be modified or extended without affecting the underlying business logic.

My experience showed me that every solution needs at least 10 different architecture patterns (or aspects) in order to be complete:

  • Authentication and authorization
  • Dependency injection
  • Localization
  • Logging
  • Auditing
  • Caching
  • Storage
  • Error handling
  • Distributed processing
  • Recurring tasks / Jobs

We won't go into detail about what each of these aspects means, instead, we will focus on how to create the architecture with them in mind and not affect the business logic that uses the underlying implementation.


Architecture requirements

There are 3 different types of aspects that we can observe from the list above: implicit, explicit, and hybrid. These types of aspects have different lifetimes and because of that, they have different implementation requirements.

Implicit aspects are the ones that exist with every handler execution and we don't need to call or use them explicitly. For example, if a call fails we retry it automatically up to 3 times with a 10-second delay.
Implicit aspects are:

  • Error handling
  • Recurring tasks / Jobs

Explicit aspects are the ones that we must use explicitly and they don't execute any underlying logic on their own. For example, we need to cache data that a complex query returns to speed up the response time.
Explicit aspects are:

  • Authentication and authorization
  • Dependency injection
  • Caching
  • Storage
  • Distributed processing

Hybrid aspects are the ones that we can use explicitly, but they are part of the implicit aspect stack as well and they do stuff on their own. For example, each time an error occurs we can log that error automatically, but we can also use the logger to log custom messages.
Hybrid aspects are:

  • Auditing
  • Logging
  • Localization

Let us review a couple of things first:

What is a handler?

A handler is a piece of code that handles a business logic request. In service pattern a service method is a handler, in CQRS both a comment and a query have a handler, and so on...

What is the difference between a handler and a web controller action?

Controller action handles a web request by converting all web-specific parts of a request (like URL params, query params, body, cookies, files, session values...) to a business logic request or a data transfer object (a DTO) which is a plain-old-C#-object (a POCO) that is not bound to an application type, meaning it can be executed in a web app context, console app context or any other kind of app.

Plug-and-play design pattern

The plug-and-play design pattern usually describes a solution that is capable to modify its execution pipeline based on the plugins that are available. This is achieved by adding, removing, or hot-swapping plugins in runtime and a detection engine that loads the available plugins and places them as part of the execution pipeline.

This approach is usually adopted by CMS and CRM solutions.

Our goal is a little bit different, we will focus on modifying the pipeline at runtime by configuration, but also swapping (NOT hot-swapping) aspect implementations. It is not our goal to detect new pipeline plugins in runtime and add them to the pipeline.

Adapter design pattern

The adapter pattern is a software design pattern (also known as wrapper, an alternative naming shared with the decorator pattern) that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.

We will use this adapter pattern to provide multiple aspect implementations that we can hot-swap as needed.


Architecture introduction

Aspect implementation

The implementation of the aspects and swapping which implementation we are using on startup. In order to achieve this, we will heavily use the adapter pattern.

Execution pipeline

The pipeline that every request goes through in which the implicit and hybrid aspects live. We will need a custom pipeline implementation in order to easily add new aspects without breaking the old ones and use the aspects at the appropriate time.

Handler context

The context in which the explicit and hybrid aspects live. We will need a custom context that will provide all available aspects and additional information that a handler can use.


In the next blog post we will create a simple AspNet project with .NET 5.0 using the CQRS architecture pattern on top of which we will build the plug-and-play architecture.

Happy coding,
DotNetGuru

Sort:  

Congratulations @dotnetguru! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

You received more than 10 upvotes.
Your next target is to reach 50 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Check out the last post from @hivebuzz:

Hive Power Up Month - Feedback from day 3
Happy New Year - Feedback from the first Hive Power Up Day of 2022
Support the HiveBuzz project. Vote for our proposal!