Software Architecture with C++

Book description

Apply business requirements to IT infrastructure and deliver a high-quality product by understanding architectures such as microservices, DevOps, and cloud-native using modern C++ standards and features

Key Features

  • Design scalable large-scale applications with the C++ programming language
  • Architect software solutions in a cloud-based environment with continuous integration and continuous delivery (CI/CD)
  • Achieve architectural goals by leveraging design patterns, language features, and useful tools

Book Description

Software architecture refers to the high-level design of complex applications. It is evolving just like the languages we use, but there are architectural concepts and patterns that you can learn to write high-performance apps in a high-level language without sacrificing readability and maintainability.

If you're working with modern C++, this practical guide will help you put your knowledge to work and design distributed, large-scale apps. You'll start by getting up to speed with architectural concepts, including established patterns and rising trends, then move on to understanding what software architecture actually is and start exploring its components.

Next, you'll discover the design concepts involved in application architecture and the patterns in software development, before going on to learn how to build, package, integrate, and deploy your components. In the concluding chapters, you'll explore different architectural qualities, such as maintainability, reusability, testability, performance, scalability, and security. Finally, you will get an overview of distributed systems, such as service-oriented architecture, microservices, and cloud-native, and understand how to apply them in application development.

By the end of this book, you'll be able to build distributed services using modern C++ and associated tools to deliver solutions as per your clients' requirements.

What you will learn

  • Understand how to apply the principles of software architecture
  • Apply design patterns and best practices to meet your architectural goals
  • Write elegant, safe, and performant code using the latest C++ features
  • Build applications that are easy to maintain and deploy
  • Explore the different architectural approaches and learn to apply them as per your requirement
  • Simplify development and operations using application containers
  • Discover various techniques to solve common problems in software design and development

Who this book is for

This software architecture C++ programming book is for experienced C++ developers looking to become software architects or develop enterprise-grade applications.

Table of contents

  1. Title Page
  2. Copyrights
    1. Software Architecture with C++
  3. Dedication
  4. Contributors
    1. About the authors
    2. About the reviewer
  5. Preface
    1. Who this book is for
    2. What this book covers
    3. To get the most out of this book
      1. Download the example code files
      2. Download the color images
      3. Conventions used
    4. Get in touch
      1. Reviews
  6. Section 1: Concepts and Components of Software Architecture
  7. Importance of Software Architecture and Principles of Great Design
    1. Technical requirements
    2. Understanding software architecture
      1. Different ways to look at architecture
    3. Learning the importance of proper architecture
      1. Software decay
      2. Accidental architecture
    4. Exploring the fundamentals of good architecture
      1. Architecture context
      2. Stakeholders
      3. Business and technical environments
    5. Developing architecture using Agile principles
      1. Domain-driven design
    6. The philosophy of C++
    7. Following the SOLID and DRY principles
      1. Single responsibility principle
      2. Open-closed principle
      3. Liskov substitution principle
      4. Interface segregation principle
      5. Dependency inversion principle
      6. The DRY rule
    8. Coupling and cohesion
      1. Coupling
      2. Cohesion
    9. Summary
    10. Questions
    11. Further reading
  8. Architectural Styles
    1. Technical requirements
    2. Deciding between stateful and stateless approaches
      1. Stateless and stateful services
    3. Understanding monoliths—why they should be avoided, and recognizing exceptions
    4. Understanding services and microservices
      1. Microservices
        1. Benefits and disadvantages of microservices
        2. Characteristics of microservices
        3. Microservices and other architectural styles
        4. Scaling microservices
        5. Transitioning to microservices
    5. Exploring event-based architecture
      1. Common event-based topologies
      2. Event sourcing
    6. Understanding layered architecture
      1. Backends for Frontends
    7. Learning module-based architecture
    8. Summary
    9. Questions
    10. Further reading
  9. Functional and Nonfunctional Requirements
    1. Technical requirements documentation from sources, you must have
    2. Understanding the types of requirements
      1. Functional requirements
      2. Nonfunctional requirements
        1. Quality attributes
        2. Constraints
    3. Recognizing architecturally significant requirements
      1. Indicators of architectural significance
      2. Hindrances in recognizing ASRs and how to deal with them
    4. Gathering requirements from various sources
      1. Knowing the context
      2. Knowing existing documentation
      3. Knowing your stakeholders
      4. Gathering requirements from stakeholders
    5. Documenting requirements
      1. Documenting the context
      2. Documenting the scope
      3. Documenting functional requirements
      4. Documenting nonfunctional requirements
      5. Managing the version history of your documentation
      6. Documenting requirements in Agile projects 
      7. Other sections
    6. Documenting architecture
      1. Understanding the 4+1 model
      2. Understanding the C4 model
      3. Documenting architecture in Agile projects
    7. Choosing the right views to document
      1. Functional view
      2. Information view
      3. Concurrency view
      4. Development view
      5. Deployment and operational views
    8. Generating documentation
      1. Generating requirements documentation
      2. Generating diagrams from code
      3. Generating (API) documentation from code
    9. Summary
    10. Questions
    11. Further reading
  10. Section 2: The Design and Development of C++ Software
  11. Architectural and System Design
    1. Technical requirements
    2. Understanding the peculiarities of distributed systems
      1. Different service models and when to use them
        1. On-premises model
        2. Infrastructure as a Service (IaaS) model
        3. Platform as a Service (PaaS) model
        4. Software as a Service (SaaS) model
        5. Function as a Service (FaaS) model and serverless architecture 
      2. Avoiding the fallacies of distributed computing
        1. The network is reliable
        2. Latency is zero
        3. Bandwidth is infinite
        4. The network is secure
        5. Topology doesn't change
        6. There is one administrator
        7. Transport cost is zero
        8. The network is homogeneous
      3. CAP theorem and eventual consistency
        1. Sagas and compensating transactions
          1. Choreography-based sagas
          2. Orchestration-based sagas
    3. Making your system fault tolerant and available
      1. Calculating your system's availability
      2. Building fault-tolerant systems
        1. Redundancy
          1. Leader election
          2. Consensus
        2. Replication
          1. Master-slave replication
          2. Multi-master replication
        3. Queue-based load leveling
        4. Back pressure
      3. Detecting faults
        1. Sidecar design pattern
        2. Heartbeat mechanism
        3. Leaky bucket counter
      4. Minimizing the impact of faults
        1. Retrying the call
        2. Avoiding cascading failures
          1. Circuit breaker
          2. Bulkhead
        3. Geodes
    4. Integrating your system
      1. Pipes and filters pattern
      2. Competing consumers
      3. Transitioning from legacy systems
        1. Anti-corruption layer
        2. Strangler pattern
    5. Achieving performance at scale
      1. CQRS and event sourcing
        1. Command-query responsibility segregation
        2. Command-query separation
        3. Event sourcing
      2. Caching
        1. Updating caches
          1. Write-through approach
          2. Write-behind approach
          3. Cache-aside
    6. Deploying your system
      1. The sidecar pattern
        1. Deploying a service with tracing and a reverse proxy using Envoy
      2. Zero-downtime deployments
        1. Blue-green deployments
        2. Canary releases
      3. External configuration store
    7. Managing your APIs
      1. API gateways
    8. Summary
    9. Questions
    10. Further reading
  12. Leveraging C++ Language Features
    1. Technical requirements
    2. Designing great APIs
      1. Leveraging RAII
      2. Specifying the interfaces of containers in C++
      3. Using pointers in interfaces
      4. Specifying preconditions and postconditions
      5. Leveraging inline namespaces
      6. Leveraging std::optional
        1. Optional function parameters
        2. Optional function return values
        3. Optional class members
    3. Writing declarative code
      1. Showcasing a featured items gallery
      2. Introducing standard ranges
        1. Reducing memory overhead and increasing performance using ranges
    4. Moving computations at compile time
      1. Helping the compiler help you by using const
    5. Leveraging the power of safe types
      1. Constraining template parameters
    6. Writing modular C++
    7. Summary
    8. Questions
    9. Further reading
  13. Design Patterns and C++
    1. Technical requirements
    2. Writing idiomatic C++
      1. Automating scope exit actions using RAII guards
      2. Managing copyability and movability
        1. Implementing non-copyable types
        2. Adhering to the rules of three and five
        3. Adhering to the rule of zero
      3. Using hidden friends
      4. Providing exception safety using the copy-and-swap idiom
      5. Writing niebloids
      6. Policy-based design idiom
    3. Curiously recurring template pattern
      1. Knowing when to use dynamic versus static polymorphism
      2. Implementing static polymorphism
      3. Interlude – using type erasure
    4. Creating objects
      1. Using factories
        1. Using factory methods
        2. Using factory functions
        3. Choosing the return type of a factory
        4. Using factory classes
      2. Using builders
        1. Building with composites and prototypes
    5. Tracking state and visiting objects in C++
    6. Dealing with memory efficiently
      1. Reducing dynamic allocations using SSO/SOO
      2. Saving memory by herding COWs
      3. Leveraging polymorphic allocators
        1. Using memory arenas
        2. Using the monotonic memory resource
        3. Using pool resources
        4. Writing your own memory resource
        5. Ensuring there are no unexpected allocations
        6. Winking out memory
    7. Summary
    8. Questions
    9. Further reading
  14. Building and Packaging
    1. Technical requirements
    2. Getting the most out of compilers
      1. Using multiple compilers
      2. Reducing build times
        1. Using a fast compiler
        2. Rethinking templates
        3. Leveraging tools
      3. Finding potential code issues
      4. Using compiler-centric tools
    3. Abstracting the build process
      1. Introducing CMake
        1. Creating CMake projects
        2. Distinguishing between CMake directory variables
        3. Specifying CMake targets
        4. Specifying the output directories
      2. Using generator expressions
    4. Using external modules
      1. Fetching dependencies
      2. Using find scripts
      3. Writing find scripts
      4. Using the Conan package manager
        1. Preparing Conan profiles
        2. Specifying Conan dependencies
        3. Installing Conan dependencies
        4. Using Conan targets from CMake
      5. Adding tests
    5. Reusing quality code
      1. Installing
      2. Exporting
      3. Using CPack
    6. Packaging using Conan
      1. Creating the conanfile.py script
      2. Testing our Conan package
      3. Adding Conan packaging code to our CMakeLists
    7. Summary
    8. Questions
    9. Further reading
  15. Section 3: Architectural Quality Attributes
  16. Writing Testable Code
    1. Technical requirements
    2. Why do you test code?
      1. The testing pyramid
      2. Non-functional testing
      3. Regression testing
      4. Root cause analysis
      5. The groundwork for further improvement
    3. Introducing testing frameworks
      1. GTest examples
      2. Catch2 examples
      3. CppUnit examples
      4. Doctest examples
      5. Testing compile-time code
    4. Understanding mocks and fakes
      1. Different test doubles
      2. Other uses for test doubles
      3. Writing test doubles
        1. GoogleMock example
        2. Trompeloeil example
    5. Test-driven class design
      1. When tests and class design clash
      2. Defensive programming
      3. The boring refrain – write your tests first
    6. Automating tests for continuous integration/continuous deployment
      1. Testing the infrastructure
      2. Testing with Serverspec
      3. Testing with Testinfra
      4. Testing with Goss
    7. Summary
    8. Questions
    9. Further reading
  17. Continuous Integration and Continuous Deployment
    1. Technical requirements
    2. Understanding CI
      1. Release early, release often
      2. Merits of CI
      3. Gating mechanism
      4. Implementing the pipeline with GitLab
    3. Reviewing code changes
      1. Automated gating mechanisms
      2. Code review – the manual gating mechanism
      3. Different approaches to a code review
      4. Using pull requests (merge requests) for a code review
    4. Exploring test-driven automation
      1. Behavior-driven development
      2. Writing tests for CI
      3. Continuous testing
    5. Managing deployment as code
      1. Using Ansible
      2. How Ansible fits with the CI/CD pipeline
      3. Using components to create deployment code
    6. Building deployment code
    7. Building a CD pipeline
      1. Continuous deployment and continuous delivery
      2. Building an example CD pipeline
    8. Using immutable infrastructure
      1. What is immutable infrastructure?
      2. The benefits of immutable infrastructure
      3. Building instance images with Packer
      4. Orchestrating the infrastructure with Terraform
    9. Summary
    10. Questions
    11. Further reading
  18. Security in Code and Deployment
    1. Technical requirements
    2. Checking the code security
      1. Security-conscious design
        1. Making interfaces easy to use and hard to misuse
        2. Enabling automatic resource management
        3. Drawbacks of concurrency and how to deal with it
      2. Secure coding, the guidelines, and GSL
      3. Defensive coding, validating everything
      4. The most common vulnerabilities
    3. Checking whether the dependencies are secure
      1. Common Vulnerabilities and Exposures
      2. Automated scanners
      3. Automated dependency upgrade management
    4. Hardening your code
      1. Security-oriented memory allocator
      2. Automated checks
        1. Compiler warnings
        2. Static analysis
        3. Dynamic analysis
          1. Valgrind and Application Verifier
          2. Sanitizers
          3. Fuzz-testing
      3. Process isolation and sandboxing
    5. Hardening your environment
      1. Static versus dynamic linking
      2. Address space layout randomization
      3. DevSecOps
    6. Summary
    7. Questions
    8. Further reading
  19. Performance
    1. Technical requirements
    2. Measuring performance
      1. Performing accurate and meaningful measurements
      2. Leveraging different types of measuring tools
      3. Using microbenchmarks
        1. Setting up Google Benchmark
        2. Writing your first microbenchmark
        3. Passing arbitrary arguments to a microbenchmark
        4. Passing numeric arguments to a microbenchmark
          1. Generating the passed arguments programmatically
        5. Choosing what to microbenchmark and optimize
        6. Creating performance tests using benchmarks
      4. Profiling
        1. Choosing the type of profiler to use
        2. Preparing the profiler and processing the results
        3. Analyzing the results
      5. Tracing
        1. Correlation IDs
    3. Helping the compiler generate performant code
      1. Optimizing whole programs
      2. Optimizing based on real-world usage patterns
      3. Writing cache-friendly code
      4. Designing your code with data in mind
    4. Parallelizing computations
      1. Understanding the differences between threads and processes
      2. Using the standard parallel algorithms
      3. Parallelizing computations using OpenMP and MPI
    5. Using coroutines
      1. Distinguishing between cppcoro utilities
      2. Looking under the hood of awaitables and coroutines
    6. Summary
    7. Questions
    8. Further reading
  20. Section 4: Cloud-Native Design Principles
  21. Service-Oriented Architecture
    1. Technical requirements
    2. Understanding Service-Oriented Arcitecture
      1. Implementation approaches
        1. Enterprise Service Bus
        2. Web services
          1. Benefits and disadvantages of web services
        3. Messaging and streaming
          1. Message queues
          2. Message brokers
        4. Cloud computing
        5. Microservices
      2. Benefits of Service-Oriented Architecture
      3. Challenges with SOA
    3. Adopting messaging principles
      1. Low-overhead messaging systems
        1. MQTT
        2. ZeroMQ
      2. Brokered messaging systems
    4. Using web services
      1. Tools for debugging web services
      2. XML-based web services
        1. XML-RPC
          1. Relationship to SOAP
        2. SOAP
          1. WSDL
          2. UDDI
          3. SOAP libraries
      3. JSON-based web services
        1. JSON-RPC
      4. REpresentational State Transfer (REST)
        1. Description languages
          1. OpenAPI
          2. RAML
          3. API Blueprint
          4. RSDL
        2. Hypermedia as the Engine of Application State
        3. REST in C++
      5. GraphQL
    5. Leveraging managed services and cloud providers
      1. Cloud computing as an extension of SOA
        1. Using API calls directly
        2. Using API calls through a CLI tool
        3. Using third-party tools that interact with the Cloud API
        4. Accessing the cloud API
        5. Using the cloud CLI
        6. Using tools that interact with the Cloud API
      2. Cloud-native architecture
    6. Summary
    7. Questions
    8. Further reading
  22. Designing Microservices
    1. Technical requirements
    2. Diving into microservices
      1. The benefits of microservices
        1. Modularity
        2. Scalability
        3. Flexibility
        4. Integration with legacy systems
        5. Distributed development
      2. Disadvantages of microservices
        1. Reliance on a mature DevOps approach
        2. Harder to debug
        3. Additional overhead
      3. Design patterns for microservices
        1. Decomposition patterns
          1. Decomposition by business capability
          2. Decomposition by subdomain
        2. Database per service pattern
        3. Deployment strategies
          1. Single service per host
          2. Multiple services per host
        4. Observability patterns
          1. Log aggregation
          2. Application metrics
          3. Distributed tracing
          4. Health check APIs
    3. Building microservices
      1. Outsourcing memory management
        1. Memcached
        2. Redis
        3. Which in-memory cache is better?
      2. Outsourcing storage
      3. Outsourcing computing
    4. Observing microservices
      1. Logging
        1. Logging with microservices
        2. Logging in C++ with spdlog
        3. Unified logging layer
          1. Logstash
          2. Filebeat
          3. Fluentd
          4. Fluent Bit
          5. Vector
        4. Log aggregation
          1. Elasticsearch
          2. Loki
        5. Log visualization
          1. Kibana
          2. Grafana
      2. Monitoring
      3. Tracing
        1. OpenTracing
        2. Jaeger
        3. OpenZipkin
      4. Integrated observability solutions
    5. Connecting microservices
      1. Application programming interfaces (APIs)
      2. Remote procedure calls
        1. Apache Thrift
        2. gRPC
    6. Scaling microservices
      1. Scaling a single service per host deployment
      2. Scaling multiple services per host deployment
    7. Summary
    8. Questions
    9. Further reading
  23. Containers
    1. Technical requirements
    2. Reintroducing containers
      1. Exploring the container types
      2. The rise of microservices
      3. Choosing when to use containers
        1. The benefits of containers
        2. The disadvantages of containers
    3. Building containers
      1. Container images explained
      2. Using Dockerfiles to build an application
      3. Naming and distributing images
      4. Compiled applications and containers
      5. Targeting multiple architectures with manifests
      6. Alternative ways to build application containers
        1. Buildah
        2. Ansible-bender
        3. Others
      7. Integrating containers with CMake
        1. Configuring the Dockerfile with CMake
        2. Integrating containers with CMake
    4. Testing and integrating containers
      1. Runtime libraries inside containers
      2. Alternative container runtimes
    5. Understanding container orchestration
      1. Self-hosted solutions
        1. Kubernetes
        2. Docker Swarm
        3. Nomad
        4. OpenShift
      2. Managed services
        1. AWS ECS
        2. AWS Fargate
        3. Azure Service Fabric
    6. Summary
    7. Questions
    8. Further reading
  24. Cloud-Native Design
    1. Technical requirements
    2. Understanding cloud-native
      1. Cloud-Native Computing Foundation
      2. Cloud as an operating system
        1. Load balancing and service discovery
          1. Reverse proxies
          2. Service Discovery
    3. Using Kubernetes to orchestrate cloud-native workloads
      1. Kubernetes structure
        1. Control plane
        2. Worker nodes
      2. Possible approaches to deploying Kubernetes
      3. Understanding the Kubernetes concepts
        1. Declarative approach
      4. Kubernetes networking
        1. Container-to-container communication
        2. Pod-to-pod communication
        3. Pod-to-service communication
        4. External-to-internal communication
      5. When is using Kubernetes a good idea?
    4. Observability in distributed systems
      1. How tracing differs from logging
      2. Choosing a tracing solution
        1. Jaeger and OpenTracing
        2. Zipkin
      3. Instrumenting an application with OpenTracing
    5. Connecting services with a service mesh
      1. Introducing a service mesh
      2. Service mesh solutions
        1. Istio
          1. Envoy
        2. Linkerd
        3. Consul service mesh
    6. Going GitOps
      1. The principles of GitOps
        1. Declarative description
        2. The system's state versioned in Git
        3. Auditable
        4. Integrated with established components
        5. Configuration drift prevention
      2. The benefits of GitOps
        1. Increased productivity
        2. Better developer experience
        3. Higher stability and reliability
        4. Improved security
      3. GitOps tools
        1. FluxCD
        2. ArgoCD
        3. Jenkins X
    7. Summary
    8. Questions
    9. Further reading
  25. Appendix A
    1. Designing data storage
      1. Which NoSQL technology should I use?
    2. Serverless architecture
    3. Communication and culture
    4. DevOps
  26. Assessments
    1. Chapter 1
    2. Chapter 2
    3. Chapter 3
    4. Chapter 4
    5. Chapter 5
    6. Chapter 6
    7. Chapter 7
    8. Chapter 8
    9. Chapter 9
    10. Chapter 10
    11. Chapter 11
    12. Chapter 12
    13. Chapter 13
    14. Chapter 14
    15. Chapter 15
  27. About Packt
    1. Why subscribe?
  28. Other Books You May Enjoy
    1. Packt is searching for authors like you
    2. Leave a review - let other readers know what you think

Product information

  • Title: Software Architecture with C++
  • Author(s): Adrian Ostrowski, Piotr Gaczkowski
  • Release date: April 2021
  • Publisher(s): Packt Publishing
  • ISBN: 9781838554590