A monthly overview of things you need to know as an architect or aspiring architects.
View an example
We protect your privacy.
Facilitating the Spread of Knowledge and Innovation in Professional Software Development
Radia Perlman describes what aspects of identity and authentication blockchain might address, and compares a “blockchain“ approach with what is deployed today.
Niosha Behnam, Sergey Fedorov tell how Netflix has shifted from geo-based DNS load-balancing to a latency-based approach relying on real-user measurements and building a model of Netflix traffic.
Montana Low provides an understanding of how Postgres can be used as a vector database for AI and how it can be integrated into your existing application stack.
Gemma Honour discusses how simple shapes can shape thinking, and break down difficult problems into something that is manageable and approachable.
Michael Friedrich discusses the learning steps with eBPF and traditional metrics monitoring and future Observability data collection, storage and visualization.
Learn practical insights on implementing & sustaining successful platform engineering programs. Register for free with code “PLATFORMENGJAN24”.
Discover new ideas and insights from senior practitioners driving change in software. Attend in-person.
Discover transformative insights to level up your software development decisions. Register now with early bird tickets.
Level up your software skills by uncovering the emerging trends you should focus on. Register now.
InfoQ Homepage Articles Advanced Architecture for ASP.NET Core Web API
This item in japanese
Jun 01, 2018 16 min read
by
reviewed by
With the release of .NET Core 2.0, Microsoft has the next major version of the general purpose, modular, cross-platform and open source platform that was initially released in 2016. .NET Core has been created to have many of the APIs that are available in the current release of .NET Framework. It was initially created to allow for the next generation of ASP.NET solutions but now drives and is the basis for many other scenarios including IoT, cloud and next generation mobile solutions. In this series, we will explore some of the benefits .NET Core and how it can benefit not only traditional .NET developers but all technologists that need to bring robust, performant and economical solutions to market.
This InfoQ article is part of the series ".NET Core". You can subscribe to receive notifications via RSS.
The Internet is a very different place from five years ago, let alone 20when I first started as a professional developer. Today, Web APIs connect the modern internetand drive both web applications and mobile apps. The skill of creating robust Web APIs that other developers can consume is in high demand. Our API’s that drive most modern web and mobile apps need to have the stability and reliability to stay servicing even when traffic is at the performance limits.
The purpose of this article is to describe the architecture of an ASP.NET Core 2.0 Web API solution using the Hexagonal Architecture and Ports and Adapters Pattern. First, we will look at the new features of .NET Core and ASP.NET Core that benefit modern Web API’s.
The solution and all code from this article’s examples can be foundin my GitHub repository ChinookASPNETCoreAPIHex.
ASP.NET Core is a new web framework that Microsoft built on top of .NET Core to shed the legacy technology that has been around since .NET 1.0. By comparison, ASP.NET 4.6 still uses the System.Webassembly that contains all the WebForms libraries and as a result is still broughtinto more recent ASP.NET MVC 5 solutions. By shedding these legacy dependencies and developing the framework from scratch, ASP.NET Core 2.0 gives the developer much better performance and is architected for cross-platform execution. With ASP.NET Core 2.0, your solutions will work as well on Linux as they do on Windows.
You can read more about the benefits of .NET Core and ASP.NET Core from the three other articles in this series. The first is Performance isa .NET Core Feature by Maarten Balliauw, ASP.NET Core – The Power of Simplicity by Chris Klug and finally, Azure and .NET Core Are Beautiful Together by Eric Boyd.
Building a great API depends ongreatarchitecture. We will be looking at many aspects of our API design and development from the built-infunctionality of ASP.NET Core to architecture philosophy and finally design patterns. There is muchplanning and thought behind this architecture, so let’s get started.
Before we dig into the architecture of our ASP.NET Core Web API solution, I want to discuss what I believe is a singlebenefit which makes .NET Core developers lives so much better; that is, DependencyInjection (DI). Now, I know you will say that we had DI in .NET Framework and ASP.NET solutions. I will agree, butthe DI we used in the past would be from third-party commercial providers or maybe open source libraries. They did a good job, butfor a good portion of .NET developers, there was a big learning curve, andall DI libraries had their uniqueway of handling things. Today with .NET Core, we have DI built right into the framework from the start. Moreover,it is quite simple to work with, andyou get it out of the box.
The reason we need to use DI in our API is that it allows usto have the best experience decoupling our architecture layers and also to allowus to mock the data layer, or have multiple data sources built for our API.
To use the .NET Core DI framework, justmake sure your project references the Microsoft.AspNetCore.AllNuGet package (which contains a dependency on Microsoft.Extnesions.DependencyInjection.Abstractionspackage). This package gives access to the IServiceCollection interface, which has a System.IServiceProvider interface that you can call GetService<TService>. Toget the services you need from the IServiceCollection interface, you will need to add the services your project needs.
To learn more about .NET Core Dependency Inject, I suggest you review the following document on MSDN: Introduction to Dependency Injection in ASP.NET Core.
We will now look at the philosophy of why we architected our API as I did. The two aspects of designing any architecture dependon these two ideas: allowing deep maintainability and utilizing proven patterns and architectures in your solutions.
Maintainability for any engineering process is the ease with which a product can be maintained: finding defects, correcting found defects, repairing or replacing defective components without having to replace still-working parts, preventing unexpected malfunctions, maximizing a product's useful life, having the ability to meet new requirements, make future maintenance easier, or cope with a changing environment.Thiscan be a difficult road to go down without a well planned and executed architecture.
Maintainability is a long-term issue and should be lookedat with a vision of your API in the far distance. With that in mind,you need to make decisions that lead to this future vision and not to short-termshortcuts that seem to make life easier right now. Making hard decisions at the start will allow your project to have a long life and provide benefits that users demand.
What makes a software architecture have high maintainability? How do we evaluate if our API canbe maintained?
The key to my API architecture is the use of C# interfaces to allow for alternative implementations. If you have written .NET code with C#,you have probably used interfaces. I use interfaces in my solution to build out a contract in my Domain layer that guarantees that any Data layer I develop for my API adheres to the contract for data repositories. It also allows the Controllers in my API project to adhere to another established contract for getting the correct methods to process the API methods in the domain project’s Supervisor. Interfaces are very important to .NET Core and if you need a refresher go here for more information.
We want our objects throughout our API solution to have single responsibilities. Thiskeeps our objects simple and easily changed if we need to fix bugs or enhance our code. If you have these “code smells” in your code,then you might be violating the single responsibility principle. As a general rule, I look at the implementations of the interface contracts for length and complexity. I do nothave a limit to the lines of code in my methods,butif you passeda single view in your IDE,it might be too long. Also,check the cyclomatic complexity of your methods to determine the complexity of your project’s methods and functions.
The Ports and Adapter Pattern (aka Hexagonal Architecture) is a way to fix this problem of having business logic coupled too tightly to other dependencies such as data access or API frameworks. Using this pattern will allow your API solution to have clear boundaries, well-namedobjects that have single responsibilities and finally allow easier development and maintainability.
We can see the pattern best visually like an onion with ports located on the outside of the hexagon and the adapters and business logic located closer to the core. I see the external connections of the architecture as the ports. The API endpoints that are consumedor the database connection used by Entity Framework Core 2.0 would be examples of ports while the internal data repositories would be the adapters.
Next,let’s look at the logical segments of our architecture and some demo code examples.
Before we look at the API and Domain layers, we need to explain how we build out the contracts through interfaces and the implementations for our API business logic. Let’s look at the Domain layer. The Domain layer has the following functions:
Our Domain Entity objects are a representation of the database that we are using to store and retrieve data used for the API business logic. Each Entity object will contain the properties represented in our case the SQL table. As an exampleis the Album entity.
The Album table in the SQL database has threecolumns: AlbumId, Title, andArtistId. These three properties are part of the Album entity as well as the Artist’s name, a collection of associated Tracks and the associated Artist. As we will see in the other layers in the API architecture, we will build upon this entity object’s definition for the ViewModels in the project.
The ViewModels are the extension of the Entities and help give more information for the consumer of the APIs. Let'slook at the Album ViewModel. It is very similar to the Album Entity but with an additional property. In the design of my API, I determined that each Album should have the name of the Artist in the payload passed back from the API. Thiswill allow the API consumer to have that crucial piece of information about the Album without having to have the Artist ViewModel passed back in the payload (especially when we are sending back a large set of Albums). An example of our AlbumViewModel is below.
The other area that is developedinto the Domain layer isthe contracts via interfaces for each of the Entities defined in the layer. Again, we will use the Album entity to show the interface that is defined.
As shown in the above example, the interface definesthe methods needed to implement the data access methods for the Album entity. Each entity object and interface are well defined and simplistic that allows the next layer to be well defined.
Finally, the core of the Domain project is the Supervisor class. Itspurpose is to translate to and from Entities and ViewModels and perform business logic away from either the API endpoints and the Data access logic. Having the Supervisor handle this also will isolate the logic to allow unit testing on the translations and business logic.
Looking at the Supervisor method for acquiring and passing a single Album to the API endpoint, we can see the logic in connecting the API front end to the data access injected into the Supervisor but still keeping each isolated.
Keeping most of the code and logic in the Domain project will allow every project to keep and adhere to the single responsibility principle.
The next layer of the API architecture we will look at is the Data Layer. In our example solution,we are using Entity Framework Core 2.0. Thiswill mean that we have the Entity Framework Core’s DBContext defined but also the Data Models generated for each entity in the SQL database. If we look at the data model for the Album entity as an example, we will see that we have three properties that are storedin the database along with a property that contains a list of associated tracks to the album, in addition to a property that contains the artist object.
While you can have a multitude of Data Layer implementations, justremember that it must adhere to the requirements documented on the Domain Layer; each Data Layer implementation must work with the View Models and repository interfaces detailed in the Domain Layer. The architecture we are developing for the API uses the Repository Pattern for connecting the API Layer to the Data Layer. Thisis done using Dependency Injection (as we discussed earlier) for each of the repository objects we implement. We will discuss how we use Dependency Injection and the code when we look at the API Layer. The key to the Data Layer is the implementation of each entity repository using the interfaces developed in the Domain Layer. Looking at the Domain Layer’s Album repository as an example shows that it implements the IAlbumRepository interface. Each repository will inject the DBContext that will allow for access to the SQL database using Entity Framework Core.
Having the Data Layer encapsulating all data access will allow facilitating a better testing story for your API. We can build multiple data access implementations: one for SQL database storage, another for maybe a cloud NoSQL storagemodel and finally a mock storage implementation for the unit tests in the solution.
The final layer that we will look at is the area that your API consumers will interact. This layer contains the code for the Web API endpoint logic including the Controllers. The API project for the solution will have a single responsibility, andthat is only to handlethe HTTP requests received by the web server and to return the HTTP responses with either success or failure. There will be a very minimal business logic in this project. We will handle exceptions and errors that have occurred in the Domain or Data projects to effectively communicate with the consumer of APIs. This communication will use HTTP response codes and any data to be returnedlocated in the HTTP response body.
In ASP.NET Core 2.0 Web API, routing is handled using Attribute Routing. If you need to learn more about Attribute Routing in ASP.NET Core go here.We are also using dependency injection tohave the Supervisor assigned toeach Controller. Each Controller’s Action method has a corresponding Supervisor method that will handle the logic for the API call. I have a segment of the Album Controller below to show these concepts.
The Web API project for the solution is very simple and thin. I strive to keep as little of code in this solution as it could be replacedwith another form of interaction in the future.
As I have demonstrated, designing and developing a great ASP.NET Core 2.0 Web API solution takes insight in order tohave a decoupled architecture that will allow each layer to be testable and follow the Single Responsibility Principle. I hope my information will allow you to create and maintain your production Web APIs for your organization’s needs.
Chris Woodruff (Woody) has a degree in Computer Science from Michigan State University’s College of Engineering. Woody has been developing and architecting software solutions for over 20 years and has worked in many different platforms and tools. He is a community leader, helping such events as GRDevNight, GRDevDay, West Michigan Day of .NET and CodeMash. He was also instrumental in bringing the popular Give Camp event to Western Michigan where technology professionals lend their time and development expertise to assist local non-profits. As a speaker and podcaster, Woody has spoken and discussed a variety of topics, including database design and open source. He has been a Microsoft MVP in Visual C#, Data Platform and SQL and was recognized in 2010 as one of the top 20 MVPs world-wide. Woody is a Developer Advocate for JetBrains and evangelizes .NET, .NET Core and JetBrains' products in North America.
With the release of .NET Core 2.0, Microsoft has the next major version of the general purpose, modular, cross-platform and open source platform that was initially released in 2016. .NET Core has been created to have many of the APIs that are available in the current release of .NET Framework. It was initially created to allow for the next generation of ASP.NET solutions but now drives and is the basis for many other scenarios including IoT, cloud and next generation mobile solutions. In this series, we will explore some of the benefits .NET Core and how it can benefit not only traditional .NET developers but all technologists that need to bring robust, performant and economical solutions to market.
This InfoQ article is part of the series ".NET Core". You can subscribe to receive notifications via RSS.
A round-up of last week’s content on InfoQ sent out every Tuesday. Join a community of over 250,000 senior developers. View an example
We protect your privacy.
You need to Register an InfoQ account or Login or login to post comments. But there’s so much more behind being registered.
Get the most out of the InfoQ experience.
Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p
by Philipp Rößner,
by Chris Woodruff,
by Philipp Rößner,
by Chris Woodruff,
by Jonathan Loscalzo,
by Chris Woodruff,
by JOel MAmedov,
by Chris Woodruff,
by Charles Cherry,
by Chris Woodruff,
by Arman Kurtagić,
by Tomasz Rzepecki,
by Tomasz Rzepecki,
by Ali Roshan,
by Tomasz Rzepecki,
by Yongmin Lee,
by Roxana Bacila,
by saurav karna,
by Zion Bokobza,
by Rajendra Verma,
by Syam Ss,
by Digvijaysinh zala,
by Philipp Rößner,
Your message is awaiting moderation. Thank you for participating in the discussion.
Nice Article! I am always checking out different types of architectures and what I like about this is that the domain-layer is implemented as the real core and one important dependency in the solution. But I am struggling with your supervisor-class. From my point of view it is violating the single responsibility principle. I am always splitting my domain-logic into classes, related to use-cases. Then you have to inject sometimes more than 1 dependency into the controller, but it feels way more controllable than having the whole domain inside of one class.
What do you think?
by JOel MAmedov,
Your message is awaiting moderation. Thank you for participating in the discussion.
Thanks for the article. I think domain layer is redundant. Your domain entities should be represented by data layer. Too many layers and lot of objects/dependencies and code. I miss old client/server days that things was much simpler. You have front end and back end.
by Chris Woodruff,
Your message is awaiting moderation. Thank you for participating in the discussion.
Thanks for the response and comment. The reason for having a three-tier architecture is to allow injecting a different data access implementation and for testing. As you can see from my ASP.NET Core Web API solution I can use Dependency Injection to inject a different data access based on my needs.
by Chris Woodruff,
Your message is awaiting moderation. Thank you for participating in the discussion.
I do agree that my Supervisor class is quite large and does not adhere to an SRP as I would want it. The reason why I had to go down that path is, so I did not get circular-references. If you look at the relationships between the data model you will see that there are tables that have associations that would cause this to happen. One example is the relationship between Artists and Albums.
I appreciate your comment, and I am always trying to find a way to have my architecture have single responsibilities when I create classes. Just was not able to for this instance.
by Philipp Rößner,
Your message is awaiting moderation. Thank you for participating in the discussion.
Isn’t it possible to to get a single incstance per Request from .Net Core DI? So you could get the same instance of the entity framework dbcontext to all your repository classes. That should avoid circular-references. Or did I get you wrong there?
by Chris Woodruff,
Your message is awaiting moderation. Thank you for participating in the discussion.
That was one of my paths that I went down and failed. I had each entity repository get the DBContext injected for the data access and ran into the circular reference. I am looking other possible solutions for the Supervisor class. Thanks
by Arman Kurtagić,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Chris! Thank you for the article, good job!
Suggestion to reconsider implementation of the domain entities or just remove them or make comment on that pointing that this is just an example of hex. arch. style showing no domain logic code which is one of the important things. Right now there is no logic in domain and they are representing anemic domain model.
That is one of the top things I see when working with junior developers. There is a risk that such implementation leads to procedural style of coding while working in oop language, meaning that important logic is likely to be scattered over the layers….
by Tomasz Rzepecki,
Your message is awaiting moderation. Thank you for participating in the discussion.
Why domain layer instead of businessLogic layer? The domain layer might suggest usage of DDD principles which are not included in this example.
by Charles Cherry,
Your message is awaiting moderation. Thank you for participating in the discussion.
RE: Separating the data access layer
I do this with the EF Core in-memory database provider. Using JSON files I can load just the data I need for testing, and then I can test out all of my data access without hitting the actual database.
by Jonathan Loscalzo,
Your message is awaiting moderation. Thank you for participating in the discussion.
I think that a “use-case” implementation would be better than supervisor.
For me, Supervisor Class is a kind of Facade Service Pattern.
And… I can’t see the HexArchitecture… ¿where are ports and adapters?
For me, it’s a simple n-layered architecture, with an entry point call Supervisor.
On the other hand, it is a cool architecture for teaching. It could continue with transactional features, interceptions, logging, etc…
Could I take it, and adapt to a class for my friends?
by Chris Woodruff,
Your message is awaiting moderation. Thank you for participating in the discussion.
Thanks and that is also a great way to have test data for unit tests. I just wanted to show a very simple technique but if I was doing more advanced testing your method would be at the top of the list. Thanks
by Chris Woodruff,
Your message is awaiting moderation. Thank you for participating in the discussion.
Yes, I started this architecture, and it is not the best representation of the Hex Architecture. I am glad you liked what I did, and I appreciate your comment.
by Tomasz Rzepecki,
Your message is awaiting moderation. Thank you for participating in the discussion.
I found team which used style from article and refused to go futher into DDD practices. They found anemic domain model as state of the art of DDD. I think its important to mention that this article is not DDD related.
by Ali Roshan,
Your message is awaiting moderation. Thank you for participating in the discussion.
This is a very informative article. Where does the cache layer fit in? I use built-in memory cache and I am rewriting our .net api into .net core 2.1. I am thinking about passing cache as dependency injection at service start up. Cache will be used with in business logic, data layer, and the api layer. Any tips regarding that?
by Tomasz Rzepecki,
Your message is awaiting moderation. Thank you for participating in the discussion.
If your business talks about cache so put it in business logic. But usually it is infrastructure concern, layer that is not mentioned in this article. Please find something about domain driven design to find how and why keep your business logic clear from infrastructure and technical concerns.
by Yongmin Lee,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hello Chris, I need your Permission to copy the content of this article to my blog. My blog is just a personal blog with a collection of good stuff like this. I am not making any profit out of this.
So, I would like to ask your permission to use the content of this article. I want to translate it into Korean and post it on my blog. For your information, my blog is just for a personal use and I am not making any profit out of it. I am definitely going to show the original link. Waiting for your comment:
by Roxana Bacila,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Yongmin,
InfoQ does not agree to any third party copying the content we publish to another platform in its entirely. We agree to posting of a summary and a link back to the InfoQ original article. We are fine with you posting a translation into Korean, if Chris is also fine with it.
Thanks,
Roxana Bacila
Community Manager
InfoQ Enterprise Software Development Community
InfoQ.com | InfoQ China | InfoQ Japan | InfoQ Brazil | InfoQ France | QCon
by saurav karna,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi Chris,
I ought to create an API and I chose dapper over Entity Framework because I needed performance.
And I was wondering what to use with Dapper, single tier or n-tier? It’s a simple API that converts data from SQL to JSON for an Angular UI. But, maybe many other functionalities will be added to the API, in the future.
I am relatively new here, so any suggestions?
by Zion Bokobza,
Your message is awaiting moderation. Thank you for participating in the discussion.
First Thanks Chris,
some remarks I have:
1. Can you please supply the interface and implementation of the above interface?
I think you meant to use IAlbumRepository.
2. You can save some roundtrip to the database in DeleteAsync function
public async Task<bool> DeleteAsync(int id, CancellationToken ct = default(CancellationToken))
{
var toRemove = _context.Album.Find(id);
if (toRemove == null)
return fasle;
_context.Album.Remove(toRemove);
await _context.SaveChangesAsync(ct);
return true;
}
3. I think the save changes should be done in another function if you want to handle transactions. maybe you want to do some changes and at the end of all changes you should call SaveChangesAsync.
Zion Bokobza
by Rajendra Verma,
Your message is awaiting moderation. Thank you for participating in the discussion.
It’s a great article.
Thanks
by Syam Ss,
Your message is awaiting moderation. Thank you for participating in the discussion.
In this sample all the layers are part of single project,otherwise data layer wont get reference of domain models. So you are preferring 3 layers in single project.
by Digvijaysinh zala,
Your message is awaiting moderation. Thank you for participating in the discussion.
hi
thank you for this blog. I want to know how to implement this type of architecture in ADO.NET with asp.net core. I will be thankful to you if you can provide in ADO.NET.
Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p
Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p
A round-up of last week’s content on InfoQ sent out every Tuesday. Join a community of over 250,000 senior developers. View an example
We protect your privacy.
April 8-10, 2024
Real-world technical talks. No product pitches.
Practical ideas to inspire you and your team.
QCon London International Software Development Conference returns on April 8-10, 2024. Level-up on 15 major software and leadership topics including; The Tech of FinTech, What’s Next in GenAI and Large Language Models (LLMs), Performance Engineering, Architecture for the Age of AI, Innovations in Data Engineering and more.
Learn the emerging trends. Explore the use cases. Implement the best practices.
SAVE YOUR SPOT NOW
InfoQ.com and all content copyright © 2006-2023 C4Media Inc.
Privacy Notice, Terms And Conditions, Cookie Policy