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.
- NoteEntity.cs is placed in the Entities folder of the Note.Domain library Note Onion - Domain - NoteEntity
- NoteTypeEntity.cs is placed in the Entities folder of the Note.Domain library Note Onion - Domain - NoteTypeEntity
- INoteRepository.cs in the Repository folder of the Note.Domain library Note Onion - 1 - Domain - INoteRepository
- INoteTypeRepository.cs in the Repository folder of the Note.Domain library Note Onion - 1 - Domain - INoteTypeRepository
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.
- NoteDto.cs is placed in the Dtos folder of the Note.Application project. Note Onion - 2 - Application - NoteDto
- NoteTypeDto.cs is placed in the Dtos folder of the Note.Application project. Note Onion - 2 - Application - NoteTypeDto
- NoteMapping.cs is placed in the Mapping folder of the Note.Application project. Note Onion - 2 - Application - NoteMapping
- NoteTypeMapping.cs is placed in the Mapping folder of the Note.Application project. Note Onion - 2 - Application - NoteTypeMapping
- INoteService.cs is placed in the Services folder of the Note.Application project. Note Onion - 2 - Application - INoteService
- INoteTypeService.cs is placed in the Services folder of the Note.Application project. Note Onion - 2 - Application - INoteTypeService
- NoteService.cs is placed in the Services folder of the Note.Application project. Note Onion - 2 - Application - NoteService
- NoteTypeService.cs is placed in the Services folder of the Note.Application project. Note Onion - 2 - Application - NoteTypeService
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.
- NoteRepository.cs is placed in the Repositories folder of the Note.Infrastructure project. Note Onion - 3 - Infrastructure - NoteRepository
- NoteTypeRepository.cs is placed in the Repositories folder of the Note.Infrastructure project. Note Onion - 3 - Infrastructure - NoteTypeRepository
- NotesDbContext.cs is placed in the Data folder of the Note.Infrastructure project. Note Onion - 3 - Infrastructure - NotesDbContext
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
- NoteController.cs is placed in the Controllers folder of the Note.Api project. Note Onion - 4 - API - NoteController
- NoteTypeController.cs is placed in the Controllers folder of the Note.Api project. Note Onion - 4 - API - NoteTypeController
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.
- NoteControllerSwashbuckleExamples.cs is placed in the root folder of the Note.Api project. Note Onion - 4 - API - NoteControllerSwashbuckeExamples
- NoteTypeControllerSwashbuckleExamples.cs is placed in the root folder of the Note.Api project. Note Onion - 4 - API - NoteTypeControllerSwashbuckeExamples
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 | CREATE TABLE [dbo].[NoteTypes]( |
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
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.