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

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

    View Slide

  2. @MarcDuiker 2
    Marc Duiker
    Sr Developer Advocate

    View Slide

  3. @MarcDuiker 3

    View Slide

  4. @MarcDuiker

    View Slide

  5. @MarcDuiker 5

    View Slide

  6. @MarcDuiker 6

    View Slide

  7. @MarcDuiker 7

    View Slide

  8. @MarcDuiker 8

    View Slide

  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.”

    View Slide

  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.”

    View Slide

  11. @MarcDuiker 11
    App Server DB Server

    View Slide

  12. @MarcDuiker 12
    Function App
    Function App Queue Storage

    View Slide

  13. @MarcDuiker 13
    Function App
    Function App Queue Storage

    View Slide

  14. @MarcDuiker 14

    View Slide

  15. @MarcDuiker 15

    View Slide

  16. @MarcDuiker 16

    View Slide

  17. @MarcDuiker

    View Slide

  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…”

    View Slide

  19. @MarcDuiker 19

    View Slide

  20. @MarcDuiker 20

    View Slide

  21. @MarcDuiker 21

    View Slide

  22. @MarcDuiker 22

    View Slide

  23. @MarcDuiker

    View Slide

  24. @MarcDuiker 24

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  29. @MarcDuiker 29

    View Slide

  30. @MarcDuiker 30

    View Slide

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

    View Slide

  32. @MarcDuiker 32

    View Slide

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

    View Slide

  34. @MarcDuiker 34
    Function App
    Website Queue

    View Slide

  35. @MarcDuiker 35

    View Slide

  36. @MarcDuiker

    View Slide

  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

    View Slide

  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.

    View Slide

  39. @MarcDuiker 39

    View Slide

  40. @MarcDuiker 40

    View Slide

  41. @MarcDuiker 41

    View Slide

  42. @MarcDuiker 42

    View Slide

  43. @MarcDuiker 43

    View Slide

  44. @MarcDuiker 44

    View Slide

  45. @MarcDuiker 45

    View Slide

  46. @MarcDuiker 46

    View Slide

  47. @MarcDuiker 47

    View Slide

  48. @MarcDuiker 48

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  54. @MarcDuiker 54

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  60. @MarcDuiker 60

    View Slide

  61. @MarcDuiker 61
    public static class SubOrchestratorExample
    {
    [FunctionName(nameof(SubOrchestratorExample))]
    public static async Task RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
    {
    var resultA = await context.CallSubOrchestratorAsync(
    nameof(SubOrchestratorA),
    null);
    var resultB = await context.CallSubOrchestratorAsync(
    nameof(SubOrchestratorB),
    resultA);
    var resultC = await context.CallSubOrchestratorAsync(
    nameof(SubOrchestratorC),
    resultC);
    return resultC;
    }
    }

    View Slide

  62. @MarcDuiker 62
    https://github.com/marcduiker/durable-functions-use-cases

    View Slide

  63. @MarcDuiker

    View Slide

  64. @MarcDuiker 64

    View Slide

  65. @MarcDuiker

    View Slide

  66. @MarcDuiker 66
    Domain
    Bounded
    Context Bounded
    Context
    Bounded
    Context
    Bounded
    Context
    https://thedomaindrivendesign.io/bounded-context/

    View Slide

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

    View Slide

  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

    View Slide

  69. @MarcDuiker 69
    Billing Shopping Basket
    https://thedomaindrivendesign.io/what-is-strategic-design

    View Slide

  70. @MarcDuiker 70

    View Slide

  71. @MarcDuiker 71

    View Slide

  72. @MarcDuiker 72

    View Slide

  73. @MarcDuiker 73
    Bounded
    Context
    Bounded
    Context

    View Slide

  74. @MarcDuiker

    View Slide

  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)

    View Slide

  76. @MarcDuiker 76

    View Slide

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

    View Slide

  78. @MarcDuiker 78

    View Slide

  79. @MarcDuiker 79

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  83. @MarcDuiker

    View Slide

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

    View Slide

  85. @MarcDuiker 85

    View Slide

  86. @MarcDuiker 86

    View Slide

  87. @MarcDuiker 87

    View Slide

  88. @MarcDuiker 88
    scale out scale out

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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?

    View Slide

  93. @MarcDuiker
    linktr.ee/marcduiker

    View Slide