Skip to content

A simple, convention-based, endpoint per action pattern implementation for AspNetCore 3.0+ with full support for Swagger

License

Notifications You must be signed in to change notification settings

dasiths/SimpleEndpoints

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SimpleEndpoints Build Master NuGet Downloads

A simple, convention-based, endpoint per action pattern implementation for AspNetCore 3.0+

Logo

Motivation

The aim of this pattern is to get away from the bloated god controllers that have a million action methods and so many dependencies. By following the SimpleEndpoints pattern we keep the endpoint scoped to a small feature and lightweight which makes it easier to understand and manage. The aim is not to blur the line between the controllers and the domain layer. You can choose to dispatch the request to the domain from the endpoint or handle it in the endpoint itself. Make an informed choice based to the context.

More about it in the blog post here.

Getting Started

  1. Install and reference the Nuget SimpleEndpoints

In the NuGet Package Manager Console, type:

    Install-Package SimpleEndpoints
  1. Define your request and response models
    public class SimpleMessage
    {
        public string Message { get; set; }
    }

    public class SimpleResponse
    {
        public string Message { get; set; }
    }
  1. Create your endpoint and implement your business logic (You can choose to handle in place or dispatch to a domain layer)
    public class SimpleMessageEndpoint : AsyncGetEndpoint<SimpleMessage, SimpleResponse>
    {
        public override async Task<ActionResult<SimpleResponse>> HandleAsync(SimpleMessage requestModel, CancellationToken cancellationToken = default)
        {	
	    // Handle in place or dispatch to the domain i.e. return await _someDomainService.HandleAsync(requestModel)
	
            return new SimpleResponse()
            {
                Message = "Hello " + requestModel.Message
            };
        }
    }
  1. In the ConfigureServices() method in your Startup.cs add the following
    public void ConfigureServices(IServiceCollection services)
    {
        // Other services go here
		
        services.AddControllers();
        services.AddSimpleEndpointsRouting(); // This is required to translate endpoint names
    }
  1. Navigate to the URL https://localhost:port_number/simplemessage?message=world and see the result.

Want more ?

Checkout the Examples folder for more. Await more examples in the coming weeks.

The Endpoints are automatically inherited from a ControllerBase and decorated with ApiController attribute. You can decorate the endpoint class/action method with the usual (Route, HttpGet, FromQuery etc) attributes to customise and extend the functionality. Endpoints fully support AspNetCore routing conventions.

If you really need an endpoint with a custom route, a mix of parameters coming from the Route/Query/Body or need full control over of any aspect then you can do something like this. Each of these class/method attributes works independently of each other and you can pick and choose them as required.

    [Route("api/some-path/[endpoint]")] //results in "api/some-path/mycustom"
    public class MyCustomEndpoint : AsyncEndpoint<Request, Result>
    {
        [NonAction] // important: mark this as non action to ignore this method when routing
        public override async Task<ActionResult<Result>> HandleAsync(Request request, CancellationToken cancellationToken = default)
        {
            // actual logic here
        }

        // definew your custom signature
        [HttpGet("custom_path")]
        public async Task<ActionResult<Result>> CustomMethod([FromQuery]string id, [FromBody]BodyModel model, CancellationToken cancellationToken)
        {
            // map to the view model
            var requestModel = new Request() {
                id_property = id,
                model_property - model
            }

            // pass to the handler method
            return await this.HandleAsync(requestModel, cancellationToken);
        }
    }

I've had good success with creating a folder structure like below.

Folder Structure

You can take this one step further and create a folder per feature group, then put each endpoint specific folder inside that if you want as well. I recommend keeping the view models in the same folder as it's easier to find related code when they sit next to each other.


Feel free to contribute and raise issues as you see fit :)

About

A simple, convention-based, endpoint per action pattern implementation for AspNetCore 3.0+ with full support for Swagger

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages