-
Notifications
You must be signed in to change notification settings - Fork 0
Overview
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 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.
With Vortex you can use all the tools and workflows that you know, just two points are recommended to be structured differently.
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.
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:
-
A testable business logic that doesn't depend on any other component (test example).
-
A centralized point that receives Dependency Injected components and executes them in a more controlled way than spread them throughout the project.
Equilaterus - Creative Commons Attribution 4.0 International (CC BY 4.0)