$30 off During Our Annual Pro Sale. View Details »

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

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

  2. @MarcDuiker 2 Marc Duiker Sr Developer Advocate

  3. @MarcDuiker 3

  4. @MarcDuiker

  5. @MarcDuiker 5

  6. @MarcDuiker 6

  7. @MarcDuiker 7

  8. @MarcDuiker 8

  9. @MarcDuiker 9 https://en.wikipedia.org/wiki/Monolithic_application “In software engineering, a monolithic application describes

    a software application that is designed without modularity.”
  10. @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.”
  11. @MarcDuiker 11 App Server DB Server

  12. @MarcDuiker 12 Function App Function App Queue Storage

  13. @MarcDuiker 13 Function App Function App Queue Storage

  14. @MarcDuiker 14

  15. @MarcDuiker 15

  16. @MarcDuiker 16

  17. @MarcDuiker

  18. @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…”
  19. @MarcDuiker 19

  20. @MarcDuiker 20

  21. @MarcDuiker 21

  22. @MarcDuiker 22

  23. @MarcDuiker

  24. @MarcDuiker 24

  25. @MarcDuiker 25 Task on backlog Work started Work completed

  26. @MarcDuiker 26 https://www.atlassian.com/incident-management/kpis/common-metrics

  27. @MarcDuiker 27 https://www.atlassian.com/incident-management/kpis/common-metrics

  28. @MarcDuiker 28 https://www.atlassian.com/incident-management/kpis/common-metrics

  29. @MarcDuiker 29

  30. @MarcDuiker 30

  31. @MarcDuiker 31 https://learn.microsoft.com/en-us/azure/azure-functions/functions-host-json

  32. @MarcDuiker 32

  33. @MarcDuiker 33 https://learn.microsoft.com/en-us/azure/azure-functions/functions-deployment-slots

  34. @MarcDuiker 34 Function App Website Queue

  35. @MarcDuiker 35

  36. @MarcDuiker

  37. @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
  38. @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.
  39. @MarcDuiker 39

  40. @MarcDuiker 40

  41. @MarcDuiker 41

  42. @MarcDuiker 42

  43. @MarcDuiker 43

  44. @MarcDuiker 44

  45. @MarcDuiker 45

  46. @MarcDuiker 46

  47. @MarcDuiker 47

  48. @MarcDuiker 48

  49. @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; } }
  50. @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; } }
  51. @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; } }
  52. @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; } }
  53. @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; } }
  54. @MarcDuiker 54

  55. @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; }
  56. @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; }
  57. @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; }
  58. @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; }
  59. @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; }
  60. @MarcDuiker 60

  61. @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; } }
  62. @MarcDuiker 62 https://github.com/marcduiker/durable-functions-use-cases

  63. @MarcDuiker

  64. @MarcDuiker 64

  65. @MarcDuiker

  66. @MarcDuiker 66 Domain Bounded Context Bounded Context Bounded Context Bounded

    Context https://thedomaindrivendesign.io/bounded-context/
  67. @MarcDuiker 67 Ecommerce Product Catalog Pricing Billing Shopping Basket https://thedomaindrivendesign.io/bounded-context/

  68. @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
  69. @MarcDuiker 69 Billing Shopping Basket https://thedomaindrivendesign.io/what-is-strategic-design

  70. @MarcDuiker 70

  71. @MarcDuiker 71

  72. @MarcDuiker 72

  73. @MarcDuiker 73 Bounded Context Bounded Context

  74. @MarcDuiker

  75. @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)
  76. @MarcDuiker 76

  77. @MarcDuiker 77 https://learn.microsoft.com/en-us/azure/architecture/patterns/

  78. @MarcDuiker 78

  79. @MarcDuiker 79

  80. @MarcDuiker 80 Function App B Function App A Queue Storage

  81. @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; } }
  82. @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; } }
  83. @MarcDuiker

  84. @MarcDuiker 84 Increase number of (virtual) machines Increase CPU /

    memory of one machine
  85. @MarcDuiker 85

  86. @MarcDuiker 86

  87. @MarcDuiker 87

  88. @MarcDuiker 88 scale out scale out

  89. @MarcDuiker 89 scale out scale out https://learn.microsoft.com/en-us/azure/azure-functions/functions-scale#scale Use dedicated resource

    to its fullest
  90. @MarcDuiker 90 scale out scale out https://learn.microsoft.com/en-us/azure/azure-functions/functions-scale#scale Use higher tier

    / sku Use regular tier / sku
  91. @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
  92. @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?
  93. @MarcDuiker linktr.ee/marcduiker