Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for An opinionated look at Minimal API in .NET 6
Oleksii Nikiforov
Oleksii Nikiforov

Posted on • Originally published atnikiforovall.github.io

     

An opinionated look at Minimal API in .NET 6

TL;DR

In this blog post, I share my thoughts on how to organize Minimal API projects to keep code structure under control and still get benefits from the low-ceremony approach.


Introduction

Minimal API is a refreshing and promising application model for building lightweight Web APIs. Now you can create a microservice and start prototyping without the necessity to create lots of boilerplate code and worrying about too much about code structure.

varapp=WebApplication.Create();app.MapGet("/",()=>"Hello World!");app.Run();
Enter fullscreen modeExit fullscreen mode

Presumably, this kind of style gives you a productivity boost and flattens the learning curve for newcomers. So it is considered as a more lightweight version, but using Minimal API doesn't mean you have to write small applications. It is rather a different application model that one day will be as much powerful as MVC counterpart.

Problem Statement

One of the problems with Minimal API is thatProgram.cs can get to big. So initial simplicity may lead you to thebig ball of mud type of solution. At this point, you want to use refactoring techniques and my goal is to share some ideas on how to tackle emerging challenges.

Example: Building Minimal API

I've prepared a demo application. I strongly recommend checking it before you move further.

Source code can be found at GitHub:NikiforovAll/minimal-api-example

Recommendations

My general recommendation is to write something that may be called Modular Minimal API or Vertical Slice Minimal API.

Keep Program.cs aka Composition Root small

A Composition Root is a unique location in an application where modules are composed together. You should have a good understanding of what this application is about just by looking at it.

You want to keep Program.cs clean and focus on high-level modules.

varbuilder=WebApplication.CreateBuilder(args);builder.AddSerilog();builder.AddSwagger();builder.AddAuthentication();builder.AddAuthorization();builder.Services.AddCors();builder.AddStorage();builder.Services.AddCarter();varapp=builder.Build();varenvironment=app.Environment;app.UseExceptionHandling(environment).UseSwaggerEndpoints(routePrefix:string.Empty).UseAppCors().UseAuthentication().UseAuthorization();app.MapCarter();app.Run();
Enter fullscreen modeExit fullscreen mode

💡Tip: One of the techniques you can apply here is to create extension methods forIServiceCollection,IApplicationBuilder. For Minimal API I would suggest using "file-per-concern" organization. SeeApplicationBuilderExtensions andServiceCollectionExtensions folders.

$tree.├── ApplicationBuilderExtensions│   ├── ApplicationBuilderExtensions.cs│   └── ApplicationBuilderExtensions.OpenAPI.cs├── assets│   └── run.http├── Features│   ├── HomeModule.cs│   └── TodosModule.cs├── GlobalUsing.cs├── MinimalAPI.csproj├── Program.cs├── Properties│   └── launchSettings.json├── ServiceCollectionExtensions│   ├── ServiceCollectionExtensions.Auth.cs│   ├── ServiceCollectionExtensions.Logging.cs│   ├── ServiceCollectionExtensions.OpenAPI.cs│   └── ServiceCollectionExtensions.Persistence.cs└── todos.db
Enter fullscreen modeExit fullscreen mode

And here is an example of how to add OpenAPI/Swagger concern:

namespaceMicrosoft.Extensions.DependencyInjection;usingMicrosoft.OpenApi.Models;publicstaticpartialclassServiceCollectionExtensions{publicstaticWebApplicationBuilderAddSwagger(thisWebApplicationBuilderbuilder){builder.Services.AddSwagger();returnbuilder;}publicstaticIServiceCollectionAddSwagger(thisIServiceCollectionservices){services.AddEndpointsApiExplorer();services.AddSwaggerGen(c=>{c.SwaggerDoc("v1",newOpenApiInfo(){Description="Minimal API Demo",Title="Minimal API Demo",Version="v1",Contact=newOpenApiContact(){Name="Oleksii Nikiforov",Url=newUri("https://github.com/nikiforovall")}});});returnservices;}}
Enter fullscreen modeExit fullscreen mode

Organize endpoints around features

AMinimalApiPlayground from Damian Edwards is a really good place to start learning more about Minimal API, but things start to get hairy (https://github.com/DamianEdwards/MinimalApiPlayground/blob/main/src/Todo.Dapper/Program.cs). Functionality by functionality you turn into a scrolling machine more and more - no good 😛. It means we need to organize code into manageable components/modules.

Modular approach allows us to focus on cohesive units of functionality. Luckily, there is an awesome open source project -Carter. It supports some essential missing features (Minimal API .NET 6) and one of them is module registrationICarterModule.

namespaceMinimalAPI;usingDapper;usingMicrosoft.Data.Sqlite;publicclassTodosModule:ICarterModule{publicvoidAddRoutes(IEndpointRouteBuilderapp){app.MapGet("/api/todos",GetTodos);app.MapGet("/api/todos/{id}",GetTodo);app.MapPost("/api/todos",CreateTodo);app.MapPut("/api/todos/{id}/mark-complete",MarkComplete);app.MapDelete("/api/todos/{id}",DeleteTodo);}privatestaticasyncTask<IResult>GetTodo(intid,SqliteConnectiondb)=>awaitdb.QuerySingleOrDefaultAsync<Todo>("SELECT * FROM Todos WHERE Id = @id",new{id})isTodotodo?Results.Ok(todo):Results.NotFound();privateasyncTask<IEnumerable<Todo>>GetTodos(SqliteConnectiondb)=>awaitdb.QueryAsync<Todo>("SELECT * FROM Todos");privatestaticasyncTask<IResult>CreateTodo(Todotodo,SqliteConnectiondb){varnewTodo=awaitdb.QuerySingleAsync<Todo>("INSERT INTO Todos(Title, IsComplete) Values(@Title, @IsComplete) RETURNING * ",todo);returnResults.Created($"/todos/{newTodo.Id}",newTodo);}privatestaticasyncTask<IResult>DeleteTodo(intid,SqliteConnectiondb)=>awaitdb.ExecuteAsync("DELETE FROM Todos WHERE Id = @id",new{id})==1?Results.NoContent():Results.NotFound();privatestaticasyncTask<IResult>MarkComplete(intid,SqliteConnectiondb)=>awaitdb.ExecuteAsync("UPDATE Todos SET IsComplete = true WHERE Id = @Id",new{Id=id})==1?Results.NoContent():Results.NotFound();}publicclassTodo{publicintId{get;set;}publicstring?Title{get;set;}publicboolIsComplete{get;set;}}
Enter fullscreen modeExit fullscreen mode

💡Tip: You can useMethod Group (C#) instead of lambda expression to avoid formatting issues and keep code clean. Also, it provides automatic endpoint metadataaspnetcore#34540, that's cool.

// FROMapp.MapGet("/todos",async(SqliteConnectiondb)=>awaitdb.QueryAsync<Todo>("SELECT * FROM Todos"));// TOapp.MapGet("/api/todos",GetTodos);asyncTask<IEnumerable<Todo>>GetTodos(SqliteConnectiondb)=>awaitdb.QueryAsync<Todo>("SELECT * FROM Todos");
Enter fullscreen modeExit fullscreen mode

To register modules you simply need to add two lines of code inProgram.cs. Modules are registered based on assemblies scanning and added to DI automatically,see. You can go even further and split Carter modules into separate assemblies.

builder.Services.AddCarter();// ...app.MapCarter();
Enter fullscreen modeExit fullscreen mode

I recommend you to enhance your Minimal APIs withCarter because it tries to close the gap between Minimal API and full-fledged ASP.NET MVC version.
Go check out Carter on GitHub,give them a Star, try it out!

High cohesion

Modules go well together withVertical Slice Architecture. Simply start with./Features folder and keep related models, services, factories, etc. together.

Conclusion

Minimal API doesn't mean your application has to be small. In this blog post, I've shared some ideas on how to handle project complexity. Personally, I like this style and believe that one day Minimal API will be as much powerful as ASP.NET MVC.


Reference

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
pjole profile image
pjole
  • Joined
• Edited on• Edited

Which is this color scheme? I like it!

CollapseExpand
 
nikiforovall profile image
Oleksii Nikiforov
Software Engineer at EPAM. Interested in .NET
  • Work
    Software Engineer
  • Joined

It's a GitHub theme, please see:github.com/primer/github-vscode-theme

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Software Engineer at EPAM. Interested in .NET
  • Work
    Software Engineer
  • Joined

More fromOleksii Nikiforov

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp