5

I'm in early steps on my Azure journey. The way I understand functions is that they're a serverless solution that is only run when actually needed. After performing their logic they "shut down" again. This is cheaper compared to something like an App Service, which is always running, even if there's no requests.

My current situation: I have a solution set up in a DDD design similar to Microsoft's eshoponcontainers solution. I have a webapi project that only has super thin controllers, that simply pass their input to Mediatr, which passes it to different projects for validation and business logic. All my controller methods look like this:

[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetById(string id)
{
    // Mediator instantiated via DI in the controller constructor
    var result = await Mediator.Send(new GetByIdQuery { Id = id });
    return Ok(result);
}

It's trivial for me to remove this webapi project, and replace it with an Azure Functions project that also takes input from an HTTP Trigger and pass it to Mediatr. My dependency injection stuff is set up in a different project, independent from webapi so that's easily inserted in my Azure Functions project as well. All my functions look like this:

public class GetId
{
    private readonly IMediator _mediator;

    public GetId(IMediator mediator)
    {
        _mediator = mediator;
    }

    [FunctionName("GetById")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] string id,
        ILogger log)
    {
        var result = await _mediator.Send(new GetByIdQuery { AgreementId = id });
        return new OkObjectResult(result);
    }
}

But isn't this terrible from a cost perspective? Based on my limited understanding, every time the Azure Function is started, it will create my substantial DI container all over again. This means I'm paying a lot for CPU time that's doing DI setup, instead of my actual business logic. Or am I missing something?

Palle Due
  • 5,929
  • 4
  • 17
  • 32
yesman
  • 7,165
  • 15
  • 52
  • 117
  • 1
    ... and you'll almost certainly find that the [cold start latency](https://azure.microsoft.com/en-gb/blog/understanding-serverless-cold-start/) of consumption / serverless Azure functions will outweigh the IoC bootstrap time and MediatR routing overhead. If the functions are called frequently, you'll likely wind up hosting them on a dedicated plan, so they won't be serverless. If you already have an app service plan with spare capacity, i'd deploy the functions there and toggle the 'Always On', thus avoiding the setup inefficiency and potentially cheaper, too. – StuartLC May 19 '21 at 10:22
  • Thanks @StuartLC , I have a long way to go. I'll read up on the material you provided. I guess the question that I also subconciously had is "will my current code translate easily to an Azure Function without too much hassle or horrible side effects". But that's something I need to figure out myself. – yesman May 19 '21 at 10:28
  • 1
    If you already have an existing investment and skill set in building api based apps hosted on AspNet, I certainly wouldn't break up my app into tons of functions just for coolness factor - it will be more difficult to maintain and manage. – StuartLC May 19 '21 at 10:36
  • 1
    I think that's a great question, but unfortunately, nobody answered objectively *yet*. I've been studying `serverless` on AWS and it changes a lot on designing the software like we have done for long time. For instance, how DDD comes into play? How boundaries comes into play? How to design functions/events/data-access? Does it make sense to have a repository in your function when you are doing one thing (accessing the dynamodb)? – JobaDiniz Feb 25 '22 at 17:06

1 Answers1

3

Leaving aside the web API vs Azure Functions discussion, what you're doing is rather bizarre: accepting a message so that you can use Mediatr to manually fire an event based on that message's contents.

Azure already has built-in functionality for exactly this: Event Grid, Event Hub, or Service Bus. Create one of these resources, publish events/messages to them instead of to a web API/Azure Functions, and replace your Mediatr event listeners with Event Grid/Event Hub/Service Bus listeners. Then you don't need a web API or Azure Functions at all.

In regards to the actual question, there are many concerns that come with Azure Functions - inability to use ASP.NET Core middleware is a big one. You might save on resource usage, but honestly Azure is so cheap that such a saving is likely to not be worth it. (I suggest you look at the actual pricing, as opposed to assuming that Azure Functions will save you money simply because it's not always-on.) And no, DI container construction doesn't cost much CPU at all - if it does, you have bigger architectural problems.

There are always tradeoffs, and nothing is free.

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
  • 1
    I don't think you understand what Mediator is (conventionally) used for. Mediator doesn't have "event listeners" it has request handlers. Request handlers are not analogous to event listeners, there must be exactly one request handler, and processing happens in-band. The original question is with regards to writing an HTTP API, the eventing infrastructure you listed is not relevant here. – Michael Fry Feb 01 '23 at 01:46