Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Help! I've created a serverless monolith! - Clo...

Marc Duiker
November 18, 2022

Help! I've created a serverless monolith! - CloudBrew

Slides for my CloudBrew 2022 session.
I talked about serverless monoliths, how unmaintainable apps cause problems with agility and how to measure agility. How accidental complexity can be reduced or moved, and I offered three strategies how to breakup a serverless monolith based on, bounded context, resilience, and scalability.

Marc Duiker

November 18, 2022
Tweet

More Decks by Marc Duiker

Other Decks in Programming

Transcript

  1. @MarcDuiker 10 https://en.wikipedia.org/wiki/Modularity “…modularity is the degree to which a

    system's components may be separated and recombined, often with the benefit of flexibility and variety in use.”
  2. @MarcDuiker 18 https://martinfowler.com/articles/agileStory.html “We eventually agreed on "agile" as we

    felt that captured the adaptiveness and response to change which we felt was so important to our approach…”
  3. @MarcDuiker 37 Caused by the nature of the (business) problem.

    Can’t be easily reduced. Caused by decisions we make related to architecture, frameworks, packages, code style etc. Can be reduced. https://en.wikipedia.org/wiki/Programming_complexity
  4. @MarcDuiker 38 https://en.wikipedia.org/wiki/Programming_complexity Caused by decisions we make related to

    architecture, frameworks, packages, code style etc. Can be reduced. Caused by the nature of the (business) problem. Can’t be easily reduced.
  5. @MarcDuiker 49 public static class ChainingExample { [FunctionName(nameof(ChainingExample))] public static

    async Task<string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var resultA = await context.CallActivityAsync<string>( nameof(ActivityFunctionA), null); var resultB = await context.CallActivityAsync<string>( nameof(ActivityFunctionB), resultA); var resultC = await context.CallActivityAsync<string>( nameof(ActivityFunctionC), resultB); return resultC; } }
  6. @MarcDuiker 50 public static class ChainingExample { [FunctionName(nameof(ChainingExample))] public static

    async Task<string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var resultA = await context.CallActivityAsync<string>( nameof(ActivityFunctionA), null); var resultB = await context.CallActivityAsync<string>( nameof(ActivityFunctionB), resultA); var resultC = await context.CallActivityAsync<string>( nameof(ActivityFunctionC), resultB); return resultC; } }
  7. @MarcDuiker 51 public static class ChainingExample { [FunctionName(nameof(ChainingExample))] public static

    async Task<string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var resultA = await context.CallActivityAsync<string>( nameof(ActivityFunctionA), null); var resultB = await context.CallActivityAsync<string>( nameof(ActivityFunctionB), resultA); var resultC = await context.CallActivityAsync<string>( nameof(ActivityFunctionC), resultB); return resultC; } }
  8. @MarcDuiker 52 public static class ChainingExample { [FunctionName(nameof(ChainingExample))] public static

    async Task<string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var resultA = await context.CallActivityAsync<string>( nameof(ActivityFunctionA), null); var resultB = await context.CallActivityAsync<string>( nameof(ActivityFunctionB), resultA); var resultC = await context.CallActivityAsync<string>( nameof(ActivityFunctionC), resultB); return resultC; } }
  9. @MarcDuiker 53 public static class ChainingExample { [FunctionName(nameof(ChainingExample))] public static

    async Task<string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var resultA = await context.CallActivityAsync<string>( nameof(ActivityFunctionA), null); var resultB = await context.CallActivityAsync<string>( nameof(ActivityFunctionB), resultA); var resultC = await context.CallActivityAsync<string>( nameof(ActivityFunctionC), resultB); return resultC; } }
  10. @MarcDuiker 55 public static class FanOutFanInExample { [FunctionName(nameof(FanOutFanInExample))] public static

    async Task<string[]> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var tasks = new List<Task<string>>(); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionA), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionB), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionC), null)); var results = await Task.WhenAll(tasks); return results; }
  11. @MarcDuiker 56 public static class FanOutFanInExample { [FunctionName(nameof(FanOutFanInExample))] public static

    async Task<string[]> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var tasks = new List<Task<string>>(); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionA), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionB), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionC), null)); var results = await Task.WhenAll(tasks); return results; }
  12. @MarcDuiker 57 public static class FanOutFanInExample { [FunctionName(nameof(FanOutFanInExample))] public static

    async Task<string[]> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var tasks = new List<Task<string>>(); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionA), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionB), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionC), null)); var results = await Task.WhenAll(tasks); return results; }
  13. @MarcDuiker 58 public static class FanOutFanInExample { [FunctionName(nameof(FanOutFanInExample))] public static

    async Task<string[]> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var tasks = new List<Task<string>>(); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionA), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionB), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionC), null)); var results = await Task.WhenAll(tasks); return results; }
  14. @MarcDuiker 59 public static class FanOutFanInExample { [FunctionName(nameof(FanOutFanInExample))] public static

    async Task<string[]> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var tasks = new List<Task<string>>(); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionA), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionB), null)); tasks.Add(context.CallActivityAsync<string>( nameof(ActivityFunctionC), null)); var results = await Task.WhenAll(tasks); return results; }
  15. @MarcDuiker 61 public static class SubOrchestratorExample { [FunctionName(nameof(SubOrchestratorExample))] public static

    async Task<string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var resultA = await context.CallSubOrchestratorAsync<string>( nameof(SubOrchestratorA), null); var resultB = await context.CallSubOrchestratorAsync<string>( nameof(SubOrchestratorB), resultA); var resultC = await context.CallSubOrchestratorAsync<string>( nameof(SubOrchestratorC), resultC); return resultC; } }
  16. @MarcDuiker 66 Domain Bounded Context Bounded Context Bounded Context Bounded

    Context https://thedomaindrivendesign.io/bounded-context/
  17. @MarcDuiker 68 “Ubiquitous language identifies the terms and concepts within

    a bounded context that are unambiguous and agreed upon by all stakeholders.” https://thedomaindrivendesign.io/what-is-strategic-design
  18. @MarcDuiker 75 “Resilience is the ability to provide and maintain

    an acceptable level of service in the face of faults and challenges to normal operation.” https://en.wikipedia.org/wiki/Resilience_(network)
  19. @MarcDuiker 81 public static class ChainingWithRetryExample { [FunctionName(nameof(ChainingWithRetryExample))] public static

    async Task<string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var resultA = await context.CallActivityWithRetryAsync<string>( nameof(ActivityFunctionA), new RetryOptions(TimeSpan.FromSeconds(5), 3) { BackoffCoefficient = 2 }, null); var resultB = await context.CallActivityAsync<string>( nameof(ActivityFunctionB), resultA); var resultC = await context.CallActivityAsync<string>( nameof(ActivityFunctionC), resultB); return resultC; } }
  20. @MarcDuiker 82 public static class ChainingWithRetryExample { [FunctionName(nameof(ChainingWithRetryExample))] public static

    async Task<string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { var resultA = await context.CallActivityWithRetryAsync<string>( nameof(ActivityFunctionA), new RetryOptions(TimeSpan.FromSeconds(5), 3) { BackoffCoefficient = 2 }, null); var resultB = await context.CallActivityAsync<string>( nameof(ActivityFunctionB), resultA); var resultC = await context.CallActivityAsync<string>( nameof(ActivityFunctionC), resultB); return resultC; } }
  21. @MarcDuiker 91 App Settings: FUNCTIONS_WORKER_PROCESS_COUNT Function App resource property: functionAppScaleLimit

    https://learn.microsoft.com/en-us/azure/azure-functions/event-driven-scaling https://learn.microsoft.com/en-us/azure/azure-functions/functions-best-practices SCALE_CONTROLLER_LOGGING_ENABLED
  22. @MarcDuiker - Monitor your agility! - Ask yourself these questions

    before adding any code: - Do *I* need to write this? - Is this a good fit with the rest of the app? - What is the impact regarding resilience/scalability of the app?