Architecture Patterns - Onion Framework

The main idea of onion architecture is to organize the application into circles or concentric layers, with each layer depending only on the inner layers.

This article is based on the following blog article I found on the Telerik website.

https://www.telerik.com/blogs/asp-net-core-basics-organizing-projects-architectural-patterns

It’s a wonderful tutorial, creating a reservation system in the MongoDb I highly recommend you read this, or at least scan it before continuing..

This page document contains quite a few differences:

  • I am creating a note system.
  • It will communicate with a SQL Server.
  • I am adding 2 objects to the microservice (Note and NoteType)
  • The telerik document represents UI and Infrastructure on the same layer. I am visualizing 4 layers in this document.

So the code is similar, but also … it’s different.

That tutorial was organised nicely, this document is organized roughly the same way.

Here’s a few more links

https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/

The Onion Arcitecture was introduced by Jeffery Palermo in 2008. This is a link to a 4 part article describing the Architecture environment. Each article is fairly short, it’s a good fast read.

https://medium.com/expedia-group-tech/onion-architecture-deed8a554423

This article by Medium is quite good. Whereas the first article does a good job with the mechanics on building an application, this article provides several insites to the framework which I like to know about. Some of those I have paraphrased in this page.

Summary

Imagine an onion, it has lots of layers each one wrapping around a layer undernieth.

This applys to this architecture in that you have several layers of your software. The outer layer represents UI, or contains the interfaces your UI program will access. The inner most layer contains the Domain logic.

There’s rules like “Inner layers cannot communicate with outer layers”.

Layers

Depending on the article, layers have different titles

Telerik Telerik Notes Medium Notes Palermo
Domain Independant of any external structures. you define your domain models, business rules and application-specific logic. Domain / Model Domain Model
Infrastructure Interacts with External Systems, structures, and Services. Can have dependencies on external libraries, frameworks, and ASP.Net Core
Domain Services Not typically crud services. Responsible for complex rules. Heart of the application Domain Services
Application This layer contains application-specific services, use cases and application logic. Application Services aka Use Cases - no business logic. We first need to calculate the price including tax computation/discounts, etc., save order items and send order confirmation notification to the customer. Pricing computation should be part of the domain service, but orchestration involving pricing computation, checking availability, saving order and notifying users should be part of the application service. The application services can be only invoked by Infrastructure services. Application Services
Infrastructure Services UI, API, Stream, Jobs, DB - Interact with the external world (External notification Service, Kafka event streams, database adapters), dont have have any logic
UI this layer includes controllers, views and other components responsible for handling HTTP requests, user input and UI rendering. The UI layer depends on the application and infrastructure layers, but should not contain any business logic. Application User Interface
Observability Services monitoring the application. Includes tasks like data stores, data collection, etc) (Kibaa, Splunk, Distructed Tracing, Alerting, DataLog)

As I look at this WOW! The telerik example has different layers! Actually some of the standard layers are in a different order then the others.

I think the point is, we develop layers, outer layers communicate with inner layers, so the choice of layering is important. Once you have decided on this layering, you should probably not change this decision.

Layers

Here is a brief description of each layer. After this we’ll discuss coding this.

Domain

This is the innermost layer.

Contains the business logic representing the core of the application.

Completely independent of any external structure.

Define domain models, business rules, and application specific logic.

Creating the solution

I created a DOS Batch file “CreateMS_Solution.Bat”

Ideally - you execute this in a developer command prompt, passing it the name of a service you want to create. For example I’m going to create a Note microservice. So you would use a command line like:

1
CreateMS_Solution Note

What this will do is create a folder called NoteOnion. In that folder it will create NoteOnion.sln, and 5 folders to hold the applications.

Folder names will be like

  • 1 - Note.Domain
  • 2 - Note.Application
  • 3 - Note.Infrastructure
  • 4 - Note.API
  • Note.Shared

One difference between this and anything I found on the internet is the presense of the numbers. I did this to make it easy to ‘work down’ the solution.

You’ll add domain classes, then application classes, then Infrastructure classes, and API classes.

I work down the list, and it’s conceptually easier to find my way through the code rather than having the solutions organized alphabetically (API, Application, Domain, Infrastructure)

Here’s a bit of information about the layers

1. The Domain Layer

This is the innermost layer.

Contains the business logic representing the core of the application.

Completely independent of any external structure.

Define domain models, business rules, and application specific logic.

2. The Application Layer

The application layer is where the service class(s) live.

It depends on the domain layer. It should not have dependancies on external frameworks.

Handles application-specific services, use cases and application logic.

3. The Infrastructure Layer

Interacts with external systems, structures and services. Includes data access, external services and other infrastructure issues.

Has dependencies on external libraries, frameworks and ASP.Net Core.

4. The API layer

As this is a Onion framework, you could consider the UI layer the outermost circle of the onion.

The UI layer includes the application’s user interface. It includes controllers, views, and other components responsible for handling HTTP requests, user input and UI rendering.

Should not contain any busines logic.

It handles user interactions and invoikes application services.

return to the applications root folder.

5. The UI Layer

The UI layer is where the HTML / or JavaScript / or typescript / or Angular code / React, or what ever UI framework will be used to develop your user interface.

This article is concerned with layers 1 through 4

The Shared layer.

Some articles on the onion framework indicate shared layers (that is a library that might be referenced by more than one layer). Others including AI suggest shared libraries are still needed.

The CreateMS_Solution.bat builds a blank shared library and adds a few references.

Ultimately, maybe this is a library wrapped up in a nuget library. In any case a shared library is created.

This article …
todo

Coding the solution

Now lets code to our solution

1. The Domain Layer

The Domain Layer is the innermost layer.

It contains the business logic representing the core of the application.

This is completely independent of any external structure.

In this layer you define domain models, business rules, and application specific logic.

Topic: You called this a NoteEntity and not just Note!

Yes, that is true.

I did that for a few reasons

  • I have a Note column and It introduces compile errors have a property the same name as the class :^)
  • I come from a monolithic environment that was not case sensitive (VB :^( ). When I see a declaration of Note I do not know if this is a variable, a class (entity, viewmodel) or some kind of reference to a UI control. Worse yet when your browsing code it’s even harder to understand.

2. The Application Layer

The application layer is where the service class(s) live.

It depends on the domain layer. It should not have dependancies on external frameworks.

Handles application-specific services, use cases and application logic.

Notes

  • DTO object means Data Transfer Objects. Another term would be ViewModel.
  • Mapping objects are used by automapper to convert Entity objects to DTO objects and visa versa.

3. The Infrastructure Layer

Interacts with external systems, structures and services. Includes data access, external services and other infrastructure issues.

Has dependencies on external libraries, frameworks and ASP.Net Core.

NotesDbContext would be the equivalent to the data layer. There are declarations of entity objects, declarations of entity associations (in the OnModelCreating). and finally a function to define a connection to the database.

4. The API layer

As this is a Onion framework, you could consider the UI layer the outermost circle of the onion. Actually since we are creating an API, then this will be named API. A UI front end would be considered an outer layer.

The UI layer includes the application’s user interface. It includes controllers, views, and other components responsible for handling HTTP requests, user input and UI rendering.

Should not contain any busines logic.

It handles user interactions and invoikes application services.

It contains the following files

The Controller classes are documented. Some standard documentation tags for Summary, parameters. But also some sample object examples. These are referenced with SwaggerResponseExample and SwaggerRequestExample annotations.

This project contains Program.cs in the root folder of the Note.Api project Note Onion - 4 - API - Program. This is executed when the API first starts up.

Finally, this project contains appsettings.json and apsettings.Development.json to hold application configurations. Eventually more configurations will be defined.

5. The UI Layer

The UI layer is where the HTML / or JavaScript / or typescript / or Angular code / React, or what ever UI framework will be used to develop your user interface.

This article is concerned with layers 1 through 4

The Shared layer.

Some articles on the onion framework indicate shared layers (that is a library that might be referenced by more than one layer). Others including AI suggest shared libraries are still needed.

The CreateMS_Solution.bat builds a blank shared library and adds a few references.

Ultimately, maybe this is a library wrapped up in a nuget library. In any case a shared library is created.

This article …
todo

The Database

Create a database named Notes.

Add tables to setup a database structure. Here is a structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
CREATE TABLE [dbo].[NoteTypes](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[NoteType] [varchar](12) NOT NULL,
[ShowOnNew] [bit] NULL
CONSTRAINT [NoteTypesShowOnNewDef] DEFAULT ((0)) ,
[CompanyKey] [varchar](255) NULL,
[ApplicationSubKey] [varchar](255) NULL,
[ApplicationKey] [varchar](255) NULL,
CONSTRAINT [NoteTypesPk] PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO

CREATE TABLE [dbo].[Notes](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Dt] [datetime] NULL
CONSTRAINT [NotesDtDef] DEFAULT (getdate()) ,
[Who] [varchar](40) NULL
CONSTRAINT [NotesWhoDef] DEFAULT (user_name()) ,
[NoteTypeId] [bigint] NOT NULL
CONSTRAINT [NoteTypeIdUsesNoteTypeFK]
FOREIGN KEY
REFERENCES [dbo].[NoteTypes] ([Id]),
[Note] [nvarchar](max) NULL,
[CompanyKey] [varchar](255) NULL,
[ApplicationSubKey] [varchar](255) NULL,
[ApplicationKey] [varchar](255) NULL,
CONSTRAINT [NotesPk] PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO

4. The UI layer

Testing

From the Application Root, execute the following

1
dotnet run --project Note.UI

dotnet will compile the code.

If any errors are found then they must be fixed.

Here is a sample
Sample Error Page

Your next step is to debug those errors. Then repeat the dotnet run command until you have resolved all errors.

Code Cleanup

Those Class1.cs files in each of those layers can be removed.

Summary

The Telerik article was great. One major takeaway was that it included both a tutorial on creating an application using the Onion Framework. But also it include a tutorial on creating a dotnet application using command lines.

What I learned about creating an application with command lines is that it’s possible and not all that difficult. I also learned that when it comes to troubleshooting the Visual Studio application makes chasing down these errors a lot simpler.

The notes project was used to help me learn how to creating Microservices. Actually this is version 4.

  • Version 1 was based on a design provided by AFShin, it consisted of 4 libraries. I created pretty much the same files that are included in this documentation, plus a number of additional files in the Shared project. (INoteRepository, INoteService, NoteDto, NoteController, NoteEntity, NoteMapping, NoteRepository, NoteService)
  • Version 2 was based on a single API library. I created a folder for Base which held the files in the shared project. I also created a folder for Note and NoteType. Those files contained pretty much the same classes that were defined in version 1. I just needed to fix the Using statements. Personally I liked this the best, I find I work on one object in all layers at the same time, then I move onto the next object. So all files I care about appear in the solution explorer in one view.
  • Version 3 is based on a design provided by Nick, it consisted of 2 or 4 libaries. Rather than having a full set of base classes, there was just one standard model class. I was able to fit the same files that were created in version 1, and 2 - just changing using statements. I also enhanced the Swashbuckle logic.
  • Version 4 is based on the Onion framework. That’s what this document is based on. 4 libraries. Same files that were created in 1, 2, and 3 - just changing using statements. One thing I noticed in Nicks code was the use of Async - great idea! so I am working on making these asyncronous methods.

If course while I’m calling the Onion Framework version of the Notes microservice a version 4. In reality it has a lot of version 1 behaviors. It’s approaching time to connect this to OAuth (I did play with Okta and Auth in the past), and more importantly setup testing.

Who knows. as I enhance this microservice, maybe I’ll freshen these documents.