Software Architecture Patterns for Serverless Systems

Book description

Key Features

  • Learn best practices for designing enterprise-grade software systems from a seasoned CTO
  • Deeper your understanding of system reliability, maintainability, and scalability
  • Elevate your skills to a professional level by learning the most effective software design patterns and architectural concepts

Book Description

As businesses are undergoing a digital transformation to keep up with competition, it is now more important than ever for IT professionals to design systems to keep up with the rate of change while maintaining stability.

This book takes you through the architectural patterns that power enterprise-grade software systems and the key architectural elements that enable change (such as events, autonomous services, and micro frontends), along with showing you how to implement and operate anti-fragile systems.

First, you’ll divide up a system and define boundaries so that your teams can work autonomously and accelerate innovation. You’ll cover low-level event and data patterns that support the entire architecture, while getting up and running with the different autonomous service design patterns.

Next, the book will focus on best practices for security, reliability, testability, observability, and performance. You’ll combine all that you've learned and build upon that foundation, exploring the methodologies of continuous experimentation, deployment, and delivery before delving into some final thoughts on how to start making progress.

By the end of this book, you'll be able to architect your own event-driven, serverless systems that are ready to adapt and change so that you can deliver value at the pace needed by your business.

What you will learn

  • Explore architectural patterns to create anti-fragile systems that thrive with change
  • Focus on DevOps practices that empower self-sufficient, full-stack teams
  • Build enterprise-scale serverless systems
  • Apply microservices principles to the frontend
  • Discover how SOLID principles apply to software and database architecture
  • Create event stream processors that power the event sourcing and CQRS pattern
  • Deploy a multi-regional system, including regional health checks, latency-based routing, and replication
  • Explore the Strangler pattern for migrating legacy systems

Who this book is for

This book is for software architects who want to learn more about different software design patterns and best practices. This isn’t a beginner’s manual – you’ll need an intermediate level of programming proficiency and software design to get started. You’ll get the most out of this software design book if you already know the basics of the cloud, but it isn’t a prerequisite.

Table of contents

  1. Software Architecture Patterns for Serverless Systems
  2. Foreword
  3. Contributors
  4. About the author
  5. About the reviewers
  6. Table of Contents
    1. Preface
    2. Section 1: Establishing an Architectural Vision
    3. Chapter 1: Architecting for Innovation
      1. Continuously delivering business value
      2. By the skin of our teeth
      3. Through high-velocity teamwork
      4. Taking control of lead time
      5. Risk mitigation
      6. Decision making
      7. Software Development Life Cycle methodology
      8. Hardware provisioning
      9. Software deployment
      10. Software structure
      11. Testing and confidence
      12. Dependencies and inter-team communication
      13. Dissecting integration styles
      14. Batch integration
      15. Spaghetti integration
      16. Real-time integration
      17. Enterprise application integration
      18. Shared database
      19. Service-oriented architecture
      20. Microservices
      21. Enabling autonomous teams with autonomous services
      22. Autonomous services – creating bulkheads
      23. Event-first – valuing facts
      24. Serverless-first – creating knowledge
      25. Data life cycle – fighting data gravity
      26. Micro frontends – equalizing tiers
      27. Observability – optimizing everything
      28. Organic evolution – embracing change
      29. Summary
    4. Chapter 2: Defining Boundaries and Letting Go
      1. Building on SOLID principles
      2. Single Responsibility Principle
      3. Open-Closed Principle
      4. Liskov Substitution Principle
      5. Interface Segregation Principle
      6. Dependency Inversion Principle
      7. Thinking about events first
      8. Event storming
      9. Verbs versus nouns
      10. Facts versus ephemeral messages
      11. Contracts versus notifications
      12. React and evolve
      13. Powered by an event hub
      14. Dividing a system into autonomous subsystems
      15. By actor
      16. By business unit
      17. By business capability
      18. By data life cycle
      19. By legacy system
      20. Creating subsystem bulkheads
      21. Decomposing a subsystem into autonomous services
      22. Context diagram
      23. Micro frontend
      24. Event hub
      25. Autonomous service patterns
      26. Dissecting an autonomous service
      27. Repository
      28. CI/CD pipeline and GitOps
      29. Tests
      30. Stack
      31. Persistence
      32. Trilateral API
      33. Functions
      34. Micro-app
      35. Shared libraries
      36. Governing without impeding
      37. Leveraging observability
      38. Facilitating a culture of robustness
      39. Audit continuously
      40. Securing the perimeter
      41. Elevating TestOps
      42. Automating account creation
      43. Summary
    5. Section 2: Dissecting the Software Architecture Patterns
    6. Chapter 3: Taming the Presentation Tier
      1. Zigzagging through time
      2. Client-side versus server-side rendering
      3. Build-time versus runtime rendering
      4. Web versus mobile
      5. Breaking up the frontend monolith
      6. By subsystem
      7. By user activity
      8. By device type
      9. By version
      10. Dissecting micro frontends
      11. The main app
      12. Micro-app
      13. Micro-app activation
      14. Mount points
      15. Manifest deployer
      16. Inter-application communication
      17. Designing for offline-first
      18. Transparency
      19. Local cache
      20. Live updates
      21. Regional failover
      22. Securing the user experience
      23. OpenID Connect
      24. Conditional rendering
      25. Protected routes
      26. Passing the JWT to BFF services
      27. Observing real user activity
      28. RUM
      29. Synthetics
      30. Summary
    7. Chapter 4: Trusting Facts and Eventual Consistency
      1. Living in an eventually consistent world
      2. Staging
      3. Consistency
      4. Concurrency and partitions
      5. Order tolerance and idempotence
      6. Parallelism
      7. Publishing to an event hub
      8. Event bus
      9. Domain events
      10. Routing and channel topology
      11. Dissecting the Event Sourcing pattern
      12. Systemwide event sourcing
      13. Event lake
      14. Event streams
      15. Micro event stores
      16. Processing event streams
      17. Batch size
      18. Functional reactive programming
      19. Unit of work
      20. Filtering and multiplexing
      21. Mapping
      22. Connectors
      23. Designing for failure
      24. Backpressure and rate limiting
      25. Poison events
      26. Fault events
      27. Resubmission
      28. Optimizing throughput
      29. Asynchronous non-blocking I/O
      30. Pipelines and multiplexing
      31. Sharding
      32. Batching and grouping
      33. Observing throughput
      34. Work metrics
      35. Iterator age
      36. Accounting for regional failover
      37. Protracted eventual consistency
      38. Regional messaging channels
      39. Summary
    8. Chapter 5: Turning the Cloud into the Database
      1. Escaping data's gravity
      2. Competing demands
      3. Insufficient capacity
      4. Intractable volumes
      5. Embracing data life cycle
      6. Create phase
      7. Use phase
      8. Analyze phase
      9. Archive phase
      10. Turning the database inside out
      11. The transaction log
      12. Derived data
      13. Dissecting the CQRS pattern
      14. Systemwide CQRS
      15. Materialized views
      16. Inbound bulkheads
      17. Live cache
      18. Capacity per reader, per query
      19. Keeping data lean
      20. Projections
      21. Time to live
      22. Implementing idempotence and order tolerance
      23. Deterministic identifiers
      24. Inverse optimistic locking
      25. Immutable event triggers
      26. Modeling data for operational performance
      27. Nodes, edges, and aggregates
      28. Sharding and partition keys
      29. Single table design examples
      30. Leveraging change data capture
      31. Database-first event sourcing
      32. Soft deletes
      33. Latching
      34. Replicating across regions
      35. Multi-master replication
      36. Round-robin replication
      37. Regional failover, protracted eventual consistency, and order tolerance
      38. Observing resource metrics
      39. Capacity
      40. Throttling and errors
      41. Performance
      42. Redacting sensitive data
      43. Envelope encryption
      44. General Data Protection Regulation (GDPR)
      45. Summary
    9. Chapter 6: A Best Friend for the Frontend
      1. Focusing on user activities
      2. A BFF service is responsible for a single user activity
      3. A BFF service is owned by the frontend team
      4. Decoupled, autonomous, and resilient
      5. Dissecting the Backend for Frontend (BFF) pattern
      6. Datastore
      7. API Gateway
      8. Query and command functions
      9. Listener function
      10. Trigger function
      11. Models and connectors
      12. Choosing between GraphQL and REST
      13. REST
      14. GraphQL
      15. Implementing different kinds of BFF services
      16. Task BFF services
      17. Search BFF services
      18. Action BFF services
      19. Dashboard BFF services
      20. Reporting BFF services
      21. Archive BFF services
      22. Securing a BFF in depth
      23. The perimeter
      24. Federated identity
      25. In transit
      26. JWT authorizer
      27. JWT assertion
      28. JWT filter
      29. Last modified by
      30. Least privilege
      31. At rest
      32. Leveraging multiple regions
      33. Latency-based routing
      34. Regional health checks
      35. Regional failover
      36. Observing BFF metrics
      37. Work metrics
      38. Throttling and concurrency limits
      39. Optimizing BFF performance
      40. Function memory allocation
      41. Cold starts
      42. Timeouts and retries
      43. cache-control
      44. Summary
    10. Chapter 7: Bridging Intersystem Gaps
      1. Creating an anti-corruption layer
      2. Dissecting the External Service Gateway pattern
      3. Connectivity
      4. Semantic transformation
      5. Action versus reaction
      6. Egress
      7. Ingress
      8. Packaging
      9. Separate cloud accounts
      10. Integrating with third-party systems
      11. Egress – API call
      12. Ingress – webhook
      13. Asynchronous request response
      14. Integrating with other subsystems
      15. Egress – upstream subsystem
      16. Ingress – downstream subsystem
      17. Integrating across cloud providers
      18. Integrating with legacy systems
      19. Ingress – Change Data Capture
      20. Egress – Direct SQL
      21. Egress – circuit breaker
      22. Ingress – relay
      23. Egress – relay
      24. Providing an external API and SPI
      25. Ingress – event
      26. Ingress – command
      27. Egress – webhook
      28. Egress – query
      29. Tackling common data challenges
      30. Idempotence
      31. Enriching data
      32. Latching and cross-referencing
      33. Slow data resync
      34. Managing shared secrets
      35. Securing secrets
      36. Using API keys
      37. Addressing multi-regional differences
      38. Egress routing and failover
      39. Ingress routing and failover
      40. Summary
    11. Chapter 8: Reacting to Events with More Events
      1. Promoting inter-service collaboration
      2. Dissecting the Control Service pattern
      3. collect
      4. correlate
      5. collate
      6. evaluate
      7. emit
      8. expire
      9. Orchestrating business processes
      10. Entry and exit events
      11. Parallel execution
      12. Employing the Saga pattern
      13. Compensating transactions
      14. Abort events
      15. Calculating event-sourcing snapshots
      16. What is ACID 2.0?
      17. Snapshot events
      18. Implementing CEP logic
      19. Decision tables
      20. Missing events
      21. Leveraging ML for control flow
      22. Models
      23. Predictions
      24. Implementing multi-regional cron jobs
      25. Job records and events
      26. Replication and idempotence
      27. Summary
    12. Section 3: Putting Everything in Motion
    13. Chapter 9: Choreographing Deployment and Delivery
      1. Optimizing testing for continuous deployment
      2. Continuous discovery
      3. Continuous testing
      4. Focusing on risk mitigation
      5. Small batch size
      6. Decoupling deployment from release
      7. Feature flags
      8. Fail forward fast
      9. Achieving zero-downtime deployments
      10. The Robustness principle
      11. Between the frontend and its backend
      12. Between producers and consumers
      13. Between the backend and its data store
      14. Planning at multiple levels
      15. Experiments
      16. Story backlog
      17. Task roadmaps
      18. Turning the crank
      19. Task branch workflow
      20. Continuous integration pipeline
      21. Continuous deployment pipeline
      22. Feature flipping
      23. Summary
    14. Chapter 10: Don't Delay, Start Experimenting
      1. Gaining trust and changing culture
      2. Establishing a vision
      3. Building momentum
      4. Constructing an architectural runway
      5. Seed and split
      6. Funding products, not projects
      7. Architecture-driven
      8. Team capacity-driven
      9. Dissecting the Strangler pattern
      10. Event-first migration
      11. Micro frontend – headless mode
      12. Retirement
      13. Addressing event-first concerns
      14. System of record versus source of truth
      15. Duplicate data is good
      16. Avoid false reuse
      17. Poly everything
      18. Polyglot programming
      19. Polyglot persistence
      20. Polycloud
      21. Summary
    15. Other Books You May Enjoy
  7. Preface
  8. Who this book is for
  9. What this book covers
  10. To get the most out of this book
  11. Download the color images
  12. Conventions used
  13. Get in touch
  14. Share Your Thoughts
  15. Section 1: Establishing an Architectural Vision
  16. Chapter 1: Architecting for Innovation
  17. Continuously delivering business value
    1. By the skin of our teeth
    2. Through high-velocity teamwork
  18. Taking control of lead time
    1. Risk mitigation
    2. Decision making
    3. Software Development Life Cycle methodology
    4. Hardware provisioning
    5. Software deployment
    6. Software structure
    7. Testing and confidence
    8. Dependencies and inter-team communication
  19. Dissecting integration styles
    1. Batch integration
    2. Spaghetti integration
    3. Real-time integration
    4. Enterprise application integration
    5. Shared database
    6. Service-oriented architecture
    7. Microservices
  20. Enabling autonomous teams with autonomous services
    1. Autonomous services – creating bulkheads
      1. Asynchronous inter-service communication
      2. Fortified boundaries
    2. Event-first – valuing facts
      1. Inversion of responsibility
      2. Events as first-class citizens
      3. Idempotence and ordered tolerance
    3. Serverless-first – creating knowledge
      1. Self-service
      2. Disposable architecture
    4. Data life cycle – fighting data gravity
    5. Micro frontends – equalizing tiers
    6. Observability – optimizing everything
    7. Organic evolution – embracing change
  21. Summary
  22. Chapter 2: Defining Boundaries and Letting Go
  23. Building on SOLID principles
    1. Single Responsibility Principle
    2. Open-Closed Principle
    3. Liskov Substitution Principle
    4. Interface Segregation Principle
    5. Dependency Inversion Principle
  24. Thinking about events first
    1. Event storming
    2. Verbs versus nouns
    3. Facts versus ephemeral messages
    4. Contracts versus notifications
    5. React and evolve
    6. Powered by an event hub
  25. Dividing a system into autonomous subsystems
    1. By actor
    2. By business unit
    3. By business capability
    4. By data life cycle
    5. By legacy system
    6. Creating subsystem bulkheads
      1. Separate cloud accounts
      2. External domain events
  26. Decomposing a subsystem into autonomous services
    1. Context diagram
    2. Micro frontend
    3. Event hub
    4. Autonomous service patterns
      1. Backend For Frontend
      2. External Service Gateways
      3. Control services
  27. Dissecting an autonomous service
    1. Repository
    2. CI/CD pipeline and GitOps
    3. Tests
    4. Stack
    5. Persistence
    6. Trilateral API
      1. Events
      2. API Gateway
    7. Functions
    8. Micro-app
    9. Shared libraries
  28. Governing without impeding
    1. Leveraging observability
      1. Work metrics
      2. Resource metrics
      3. System events
      4. Team metrics
      5. Cost metrics
    2. Facilitating a culture of robustness
    3. Audit continuously
    4. Securing the perimeter
    5. Elevating TestOps
    6. Automating account creation
  29. Section 2: Dissecting the Software Architecture Patterns
  30. Chapter 3: Taming the Presentation Tier
  31. Zigzagging through time
    1. Client-side versus server-side rendering
    2. Build-time versus runtime rendering
    3. Web versus mobile
  32. Breaking up the frontend monolith
    1. By subsystem
    2. By user activity
    3. By device type
    4. By version
  33. Dissecting micro frontends
    1. The main app
      1. The index file
      2. The importmap file
      3. Micro-app registration
    2. Micro-app
      1. The entry file
      2. Root component
    3. Micro-app activation
      1. Micro-app life cycle
      2. Route-based activation
      3. Manual activation
    4. Mount points
    5. Manifest deployer
    6. Inter-application communication
  34. Designing for offline-first
    1. Transparency
      1. Status indicators
      2. Outbox
      3. Inbox
    2. Local cache
      1. Installation
      2. Read-through cache
      3. Write-through cache
    3. Live updates
      1. WebSocket
      2. Long polling
    4. Regional failover
  35. Securing the user experience
    1. OpenID Connect
    2. Conditional rendering
    3. Protected routes
    4. Passing the JWT to BFF services
  36. Observing real user activity
    1. RUM
    2. Synthetics
  37. Chapter 4: Trusting Facts and Eventual Consistency
  38. Living in an eventually consistent world
    1. Staging
      1. Cooperative
      2. Atomic
    2. Consistency
      1. Transparency
      2. Facts
      3. Chain reaction
    3. Concurrency and partitions
    4. Order tolerance and idempotence
    5. Parallelism
  39. Publishing to an event hub
    1. Event bus
    2. Domain events
      1. Event envelope
      2. Event carried state transfer
      3. Substitution
      4. Internal versus external
    3. Routing and channel topology
  40. Dissecting the Event Sourcing pattern
    1. Systemwide event sourcing
    2. Event lake
      1. Perpetual storage
      2. Indexing events
      3. Replaying events
    3. Event streams
      1. Temporal storage
      2. Stream-first event sourcing
    4. Micro event stores
  41. Processing event streams
    1. Batch size
    2. Functional reactive programming
    3. Unit of work
    4. Filtering and multiplexing
    5. Mapping
    6. Connectors
  42. Designing for failure
    1. Backpressure and rate limiting
    2. Poison events
    3. Fault events
    4. Resubmission
  43. Optimizing throughput
    1. Batch size
    2. Asynchronous non-blocking I/O
    3. Pipelines and multiplexing
      1. Pipeline patterns
    4. Sharding
    5. Batching and grouping
      1. Batching
      2. Grouping
  44. Observing throughput
    1. Work metrics
    2. Iterator age
    3. Fault events
  45. Accounting for regional failover
    1. Protracted eventual consistency
    2. Regional messaging channels
  46. Chapter 5: Turning the Cloud into the Database
  47. Escaping data's gravity
    1. Competing demands
    2. Insufficient capacity
    3. Intractable volumes
  48. Embracing data life cycle
    1. Create phase
    2. Use phase
    3. Analyze phase
    4. Archive phase
  49. Turning the database inside out
    1. The transaction log
    2. Derived data
  50. Dissecting the CQRS pattern
    1. Systemwide CQRS
    2. Materialized views
    3. Inbound bulkheads
    4. Live cache
    5. Capacity per reader, per query
  51. Keeping data lean
    1. Projections
    2. Time to live
  52. Implementing idempotence and order tolerance
    1. Deterministic identifiers
    2. Inverse optimistic locking
    3. Immutable event triggers
  53. Modeling data for operational performance
    1. Nodes, edges, and aggregates
    2. Sharding and partition keys
    3. Single table design examples
      1. Restaurant service
      2. Customer service
      3. Cart service
      4. Delivery service
  54. Leveraging change data capture
    1. Database-first event sourcing
    2. Soft deletes
    3. Latching
  55. Replicating across regions
    1. Multi-master replication
    2. Round-robin replication
    3. Regional failover, protracted eventual consistency, and order tolerance
  56. Observing resource metrics
    1. Capacity
    2. Throttling and errors
    3. Performance
  57. Redacting sensitive data
    1. Envelope encryption
    2. General Data Protection Regulation (GDPR)
  58. Chapter 6: A Best Friend for the Frontend
  59. Focusing on user activities
    1. A BFF service is responsible for a single user activity
    2. A BFF service is owned by the frontend team
    3. Decoupled, autonomous, and resilient
  60. Dissecting the Backend for Frontend (BFF) pattern
    1. Datastore
    2. API Gateway
    3. Query and command functions
    4. Listener function
    5. Trigger function
    6. Models and connectors
  61. Choosing between GraphQL and REST
    1. REST
    2. GraphQL
  62. Implementing different kinds of BFF services
    1. Task BFF services
    2. Search BFF services
    3. Action BFF services
    4. Dashboard BFF services
    5. Reporting BFF services
    6. Archive BFF services
  63. Securing a BFF in depth
    1. The perimeter
    2. Federated identity
    3. In transit
    4. JWT authorizer
    5. JWT assertion
    6. JWT filter
    7. Last modified by
    8. Least privilege
    9. At rest
  64. Leveraging multiple regions
    1. Latency-based routing
    2. Regional health checks
    3. Regional failover
  65. Observing BFF metrics
    1. Work metrics
    2. Throttling and concurrency limits
  66. Optimizing BFF performance
    1. Function memory allocation
    2. Cold starts
    3. Timeouts and retries
    4. cache-control
  67. Chapter 7: Bridging Intersystem Gaps
  68. Creating an anti-corruption layer
  69. Dissecting the External Service Gateway pattern
    1. Connectivity
    2. Semantic transformation
    3. Action versus reaction
    4. Egress
    5. Ingress
    6. Packaging
    7. Separate cloud accounts
  70. Integrating with third-party systems
    1. Egress – API call
    2. Ingress – webhook
    3. Asynchronous request response
  71. Integrating with other subsystems
    1. Egress – upstream subsystem
    2. Ingress – downstream subsystem
  72. Integrating across cloud providers
  73. Integrating with legacy systems
    1. Ingress – Change Data Capture
    2. Egress – Direct SQL
    3. Egress – circuit breaker
    4. Ingress – relay
      1. Messages
      2. Files
    5. Egress – relay
      1. Messages
      2. Files
  74. Providing an external API and SPI
    1. Ingress – event
    2. Ingress – command
    3. Egress – webhook
    4. Egress – query
  75. Tackling common data challenges
    1. Idempotence
    2. Enriching data
    3. Latching and cross-referencing
    4. Slow data resync
  76. Managing shared secrets
    1. Securing secrets
    2. Using API keys
  77. Addressing multi-regional differences
    1. Egress routing and failover
    2. Ingress routing and failover
  78. Chapter 8: Reacting to Events with More Events
  79. Promoting inter-service collaboration
  80. Dissecting the Control Service pattern
    1. collect
    2. correlate
    3. collate
    4. evaluate
    5. emit
    6. expire
  81. Orchestrating business processes
    1. Entry and exit events
    2. Parallel execution
  82. Employing the Saga pattern
    1. Compensating transactions
    2. Abort events
  83. Calculating event-sourcing snapshots
    1. What is ACID 2.0?
    2. Snapshot events
  84. Implementing CEP logic
    1. Decision tables
    2. Missing events
  85. Leveraging ML for control flow
    1. Models
    2. Predictions
  86. Implementing multi-regional cron jobs
    1. Job records and events
    2. Replication and idempotence
  87. Section 3: Putting Everything in Motion
  88. Chapter 9: Choreographing Deployment and Delivery
  89. Optimizing testing for continuous deployment
    1. Continuous discovery
    2. Continuous testing
  90. Focusing on risk mitigation
    1. Small batch size
    2. Decoupling deployment from release
    3. Feature flags
    4. Fail forward fast
  91. Achieving zero-downtime deployments
    1. The Robustness principle
    2. Between the frontend and its backend
    3. Between producers and consumers
    4. Between the backend and its data store
  92. Planning at multiple levels
    1. Experiments
    2. Story backlog
      1. Story mapping
      2. Story planning
    3. Task roadmaps
  93. Turning the crank
    1. Task branch workflow
      1. Create task branch
      2. Create draft pull request
      3. Ready for review
      4. Merge to master
      5. Accept canary deployment
    2. Continuous integration pipeline
      1. Unit testing
        1. Coverage
        2. Static analysis
        3. Mock objects
      2. Integration testing
        1. VCR test double
      3. Contract testing
        1. Synchronous
        2. Asynchronous
      4. Transitive end-to-end testing
    3. Continuous deployment pipeline
      1. Regional canary deployments
      2. Synthetics
      3. Anomaly detection
    4. Feature flipping
      1. Exploratory testing
      2. Beta users
      3. General availability
  94. Chapter 10: Don't Delay, Start Experimenting
  95. Gaining trust and changing culture
    1. Establishing a vision
    2. Building momentum
    3. Constructing an architectural runway
    4. Seed and split
  96. Funding products, not projects
    1. Architecture-driven
    2. Team capacity-driven
  97. Dissecting the Strangler pattern
    1. Event-first migration
    2. Micro frontend – headless mode
    3. Retirement
  98. Addressing event-first concerns
    1. System of record versus source of truth
    2. Duplicate data is good
    3. Avoid false reuse
  99. Poly everything
    1. Polyglot programming
    2. Polyglot persistence
    3. Polycloud
  100. Why subscribe?
  101. Other Books You May Enjoy
  102. Packt is searching for authors like you

Product information

  • Title: Software Architecture Patterns for Serverless Systems
  • Author(s): John Gilbert
  • Release date: July 2021
  • Publisher(s): Packt Publishing
  • ISBN: 9781800207035