Skip to content

Overview

Daniel Cañizares Corrales edited this page Feb 24, 2021 · 8 revisions

With a monadic architecture your applications are going to have a pure business logic that allows you to write easy tests and do not depend of any other layer.

Also, your side effects are going to be centralized. Instead of injecting the dependencies throughout your project, you just have to inject the behavior with the monads giving live the pure business logic interacting with the exterior world.

Vortex purpose

Vortex is a framework to handle modern development complexity using functional programming concepts and structures. Basically you can use it with any other .Net Core library as Vortex is completely agnostic to your .Net tech stack.

Guidelines for web development

With Vortex you can use all the tools and workflows that you know, just two points are recommended to be structured differently.

Business logic

Your business logic should be static and built with Pure Functions.

public static Maybe<Product> TryOrder(Product product, int quantity)
{
    if (quantity > product.AvailableUnits)
    {
        return new Maybe<Product>();
    }

    // Note that we don't modify the product param
    // instead, create a new instance to keep pure
    // this function
    var newProduct = new Product(product);
    newProduct.AvailableUnits -= quantity;
    return new Maybe<Product>(newProduct);            
}

See full example.

Controllers

Your controllers should be built with Vortex Monands, they're the glue for your components.

[HttpPost]
public async Task<IActionResult> Order(OrderCommand orderCommand)
{
return await
    // Try to generate an order
    from maybeOrder in
        // Use a normal data layer to get your data (EF or anything else)
        from product in _repository.FindByIdAsync(orderCommand.ProductId)
        // Apply your Pure Business Logic
        select OrderBehavior.TryOrder(product, orderCommand.Quantity)

    // Update the DB awaiting a side effect for maybeOrder
    from result in maybeOrder.AwaitSideEffect(_repository.UpdateAsync)
    
    // Return results
    select result.Match<IActionResult>(
        Ok,
        StatusCode(500, new { Error = "No available units." }));
}

See full example.

  • Controllers are the point where you should use Dependency Injection to receive things like Repositories, Automapper or any other component with Side-Effects
  • We encourage you to use a Global Error handling, in that way you can set-up a common error code framework to interact with client application instead of using special cases for each endpoint of your API.

By following those rules you will have:

  1. A testable business logic that doesn't depend on any other component (test example).

  2. A centralized point that receives Dependency Injected components and executes them in a more controlled way than spread them throughout the project.

Clone this wiki locally