Software Architecture with C++. Designing robust C++ systems with modern architectural practices (2nd Edition)
Год издания: 2025
Автор: Andrey Gavrilin, Adrian Ostrowski, Piotr Gaczkowski
Жанр или тематика: Программирование
Издательство: Packt
ISBN: 978-1-80324-301-6
Язык: Английский
Формат: EPUB
Качество: Издательский макет или текст (eBook)
Интерактивное оглавление: Да
Количество страниц: 738
Описание:
Key benefits
- Design modern C++ systems using features from C++11 through C++20 and beyond
- Explore distributed system design with observability, scalability, and maintainability in mind
- Manage and package C++ applications, and prepare them for deployment using CMake, Conan, and CI/CD
Description
Designing scalable and maintainable software with C++ requires more than language expertise—it demands strong architectural thinking. This practical guide equips you with the skills to design and build robust, distributed systems using modern C++. Starting with fundamental architectural principles and design philosophies, the book walks you through practical approaches to designing and deploying reliable systems. This edition contains significant updates across the book, including new chapters on observability, package management, and C++ modules to address real-world software challenges. You will explore software decomposition strategies, design and system patterns, fault tolerance, API management, and testability—all applied with C++. Additionally, the book covers modern CI/CD pipelines, cloud-native design, microservices, and modular development, helping developers navigate today's fast-evolving software landscape. With updated examples and a renewed emphasis on maintainable and observable architectures, this edition equips C++ professionals to architect modern systems. By the end of this book, you will be able to design, build, test, and deploy well-architected solutions using modern C++ and proven architectural techniques. *Email sign-up and proof of purchase required
Who is this book for?
This book is intended for experienced C++ developers and software engineers aiming to expand their architectural knowledge, lead software projects, or build scalable systems. It assumes readers are comfortable with modern C++ (C++11 onwards) and familiar with basic design principles and patterns.
What you will learn
- Apply architectural fundamentals to design scalable C++ systems
- Use modern C++ features to create maintainable and secure applications
- Implement architectural and system design patterns
- Design testable code and automate quality checks via CI/CD pipelines
- Manage dependencies and build systems using CMake and Conan
- Explore microservices, containers, and cloud-native practices in C++
- Improve observability with logging, tracing, and monitoring tools
- Build deployable C++ systems suited for modern development environments
Оглавление
Contributors
Join us on Discord!
Preface
Who this book is for
What this book covers
To get the most out of this book
Get in touch
Concepts and Components of Software Architecture
Importance of Software Architecture and Principles of Great Design
Getting the most out of this book – get to know your free benefits
Next-gen reader
Interactive AI assistant (beta)
DRM-free PDF or ePub version
Technical requirements
Understanding software architecture
Different ways to look at architecture
Communication and culture
The importance of proper architecture
Technical debt
Accidental architecture
The fundamentals of good architecture
Architecture context
Stakeholders
Business and technical environments
Developing architecture using Agile principles
Domain-driven design
The philosophy of C++
Following the SOLID and DRY principles
SOLID principles
Single responsibility principle
Open–closed principle
Liskov substitution principle
Interface segregation principle
Dependency inversion principle
The DRY rule
Coupling and cohesion
Coupling
Cohesion
Summary
Questions
Further reading
Architectural Styles
Technical requirements
Deciding between stateful and stateless approaches
Stateless and stateful services
Understanding monoliths
Understanding services and microservices
Service-oriented architecture
Microservices
Characteristics of microservices
Benefits and disadvantages of microservices
Scaling microservices
Transitioning to microservices
Exploring event-based architecture
Exploring the event sourcing pattern
Understanding layered architecture
Backends for frontends
Learning module-based architecture
Introducing hexagonal architecture
Summary
Questions
Further reading
Functional and Non-Functional Requirements
Technical requirements
Understanding the types of requirements
Functional requirements
Non-functional requirements
Quality attributes
Constraints
Recognizing architecturally significant requirements (ASRs)
Indicators of architectural significance
Hindrances in recognizing ASRs and how to deal with them
Gathering requirements
Knowing the context
Knowing existing documentation
Knowing your stakeholders
Gathering requirements from stakeholders
Documenting requirements
Documenting the context
Documenting the scope
Documenting functional requirements
Documenting NFRs
Managing the version history of your documentation
Documenting requirements in Agile projects
Other sections
Documenting architecture
Understanding the 4+1 model
Understanding the C4 model
Documenting architecture in Agile projects
Choosing the right views to document
Functional view
Information view
Concurrency view
Development view
Deployment and operational views
Generating documentation
Generating requirements documentation
Generating diagrams from code
Generating (API) documentation from code
Summary
Questions
Further reading
The Design and Development of C++ Software
Architectural and System Design Patterns
Technical requirements
Understanding the peculiarities of distributed systems
Different service models and when to use them
On-premises model
Infrastructure as a Service model
Platform as a Service model
Software as a Service model
Function as a Service model and serverless architecture
Avoiding the fallacies of distributed computing
The network is reliable
Latency is zero
Bandwidth is infinite
The network is secure
Topology doesn’t change
There is one administrator
Transport cost is zero
The network is homogeneous
CAP theorem and eventual consistency
Saga and compensating transaction patterns
Making your system fault-tolerant and available
Calculating your system’s availability
Building fault-tolerant systems
Redundancy patterns
Replication patterns
Queue-based load leveling pattern
Back pressure pattern
Detecting faults
Sidecar design pattern
Heartbeat mechanism pattern
Leaky bucket counter pattern
Minimizing the impact of faults
Retrying the call (retry pattern)
Avoiding cascading failures
Geodes pattern
Integrating your system
Pipes and filters pattern
Competing consumers pattern
Transitioning from legacy systems
Anti-corruption layer pattern
Strangler pattern
Achieving performance at scale
CQRS and event-sourcing patterns
CQRS
Command query separation
Event sourcing
Caching patterns
Updating caches
Designing data storage
Which NoSQL technology should I use?
Deploying your system
The sidecar pattern
Deploying a service with tracing and a reverse proxy using Envoy
Zero-downtime deployments
Blue–green deployment pattern
Canary release pattern
External configuration store pattern
Managing your APIs
API management tools
API gateways
Summary
Questions
Further reading
Leveraging C++ Language Features
Technical requirements
Using RAII
Specifying the interfaces of containers in C++
Using raw and smart pointers in interfaces
Specifying preconditions and postconditions
Employing inline namespaces
Applying std::optional
Optional function parameters
Optional function return values
Optional class members
Using monadic interfaces
Writing declarative code
Showcasing a featured items gallery
Applying standard ranges
Moving computations to compile time
Helping the compiler help you by using const
Leveraging the power of safe types
Constraining template parameters
Summary
Questions
Further reading
Design Patterns and C++ Idioms
Technical requirements
Writing idiomatic C++
Automating scope exit actions using RAII guards
Managing copyability and movability
Implementing non-copyable types
Adhering to the rules of three and five
Adhering to the rule of zero
Using hidden friends
Providing exception safety using the copy-and-swap idiom
Writing niebloids
Using the policy-based design idiom
Applying the curiously recurring template pattern
Knowing when to use dynamic versus static polymorphism
Implementing static polymorphism
Interlude: using type erasure
Implementing deducing this
Creating objects
Using factories
Using factory methods
Using factory functions
Choosing the return type of a factory
Using factory classes
Using builders
Using prototypes
Tracking state and visiting objects in C++
Dealing with memory efficiently
Reducing dynamic allocations using SSO/SBO/SOO
Saving memory by herding COWs
Leveraging polymorphic allocators
Using memory arenas
Using the monotonic memory resource
Using pool resources
Writing your own memory resource
Ensuring there are no unexpected allocations
Winking out memory
Summary
Questions
Further reading
Building and Packaging
Technical requirements
Getting the most out of compilers
Using multiple compilers
Reducing build times
Using a fast compiler
Rethinking templates
Leveraging tools
Finding potential code issues
Using compiler-centric tools
Abstracting the build process
Getting started with CMake
Creating CMake projects
Distinguishing between CMake directory variables
Specifying CMake targets
Specifying the output directories
Using generator expressions
Using external modules
Fetching dependencies
Using find scripts
Writing find scripts
Adding tests
Reusing quality code
Installing
Exporting
Using CPack
Summary
Questions
Further reading
Package Management
Technical requirements
Introduction to package management
Using the Conan package manager
Preparing Conan profiles
Specifying Conan dependencies
Installing Conan dependencies
Using Conan targets from CMake
Packaging using Conan
Testing our Conan package
Creating and publishing our Conan package
Summary
Questions
Further reading
The Future of C++ Code Reuse: Using Modules
Technical requirements
Introduction to C++ modules
Consuming modules
Creating your own modules
Migrating existing code bases to modules
Using modules with CMake
Using modules with Conan
Summary
Questions
Further reading
Architectural Quality Attributes
Writing Testable Code
Technical requirements
Why do you test code?
The testing pyramid
Non-functional testing
Regression testing
Root cause analysis
The groundwork for further improvement
Runtime testing frameworks
GTest example
Catch2 example
CppUTest example
Doctest example
Boost.Test example
DrogonTest example
[Boost].UT example
Compile-time testing techniques
Understanding test doubles
Different test doubles
Writing test doubles
gMock example
Trompeloeil example
CppUMock example
Test-driven class design
When tests and class design clash
The boring refrain: write your tests first
Defensive programming
Automating application tests for CI/CD
Automating infrastructure validation for CI/CD
Testing with Serverspec
Testing with Testinfra
Testing with Goss
Summary
Questions
Further reading
Continuous Integration and Continuous Deployment
Technical requirements
Understanding and implementing CI
Release early, release often
Merits of CI
Implementing the pipeline with GitLab
Exploring gating mechanisms
Reviewing code changes
Different approaches to a code review
Using pull requests (merge requests) for a code review
Exploring test-driven automation
Behavior-driven development
Writing tests for CI
Continuous testing
Managing deployment as code
Using Ansible
How Ansible fits with the CI/CD pipeline
Using reusable components (roles) and playbooks in Ansible
Building deployment code
Understanding and implementing CD
Building an example CD pipeline with GitLab
Implementing a pre-commit workflow with GitHub Actions
Implementing the pipeline with GitHub Actions
Using immutable infrastructure
What is immutable infrastructure?
Benefits and practical considerations of immutable infrastructure
Building instance images with Packer
Orchestrating the infrastructure with Terraform
Summary
Questions
Further reading
Security in Code and Deployment
Technical requirements
Security-conscious design
Making interfaces easy to use and hard to misuse
Enabling automatic resource management
Dealing with the drawbacks of concurrency
Secure coding guidelines and GSL
Defensive coding and validating everything
The most common vulnerabilities
Checking whether the dependencies are secure
Using the CVE list
Using automated scanners
Automated dependency upgrade management
Hardening your code
Security-oriented memory allocator
Automated checks
Compiler warnings
Static analysis
Dynamic analysis
Process isolation and sandboxing
Hardening your environment
Linking strategies and their security implications
Address space layout randomization
DevSecOps
Summary
Questions
Further reading
Performance
Technical requirements
Measuring performance
Preparing for accurate and repeatable performance measurements
Leveraging different types of measuring tools
Using microbenchmarks
Setting up Google Benchmark
Writing your first microbenchmark
Reusing microbenchmark logic via argument capture
Varying input sizes using microbenchmark arguments
Benchmarking with Catch2
Benchmarking with nanobench
Choosing what to microbenchmark and optimize
Creating performance tests using benchmarks
Performance profiling
Choosing the type of performance profiler to use
Preparing the environment and processing the results
Analyzing the results
Tracing
Helping the compiler generate performant code
Optimizing whole programs
Optimizing based on real-world usage patterns
Writing cache-friendly code
Designing your code with data in mind
Parallelizing computations
Choosing between threads and processes
Using the standard parallel algorithms
Using OpenMP and MPI
Other platforms and tools for parallel programming
Using coroutines
Distinguishing between libcoro utilities
Looking under the hood of awaitables and coroutines
A practical example using libcoro
A practical example using coost
A practical example using Boost.Cobalt
Implementing efficient algorithms
Summary
Questions
Further reading
Cloud-Native Design Principles
Architecture of Distributed Systems
Technical requirements
Understanding SOA
Architectural implementations
ESB
Web services
Messaging and streaming
Cloud computing
Microservices
Benefits of SOA
Challenges with SOA
Diving into microservices
The benefits of microservices
Modularity
Scalability
Flexibility
Integration with legacy systems
Distributed development
Disadvantages of microservices
Reliance on a mature DevOps approach
Harder to debug
Additional overhead
Design patterns for microservices
Decomposition patterns
Database per service pattern
Deployment strategies
Design considerations for scalable microservices
Addressing scalability bottlenecks
Outsourcing memory management
Outsourcing storage
Outsourcing computing
Scaling microservices
Scaling a single service per host deployment
Scaling multiple services per host deployment
Leveraging managed services and cloud providers
Cloud computing as an extension of SOA
Accessing the cloud API
Using cloud provider APIs
Using API calls directly
Using API calls through a CLI tool
Using third-party tools that interact with the cloud API
Using the cloud CLI
Using tools that interact with the cloud API
Summary
Questions
Further reading
Interservice Communication
Technical requirements
Introduction to interservice communication
Interaction styles
Messaging systems
Low-overhead messaging systems
MQTT
ZeroMQ
Brokered messaging systems
Apache Kafka
RabbitMQ
Using web services
Tools for working with web services
Web API payload formats
XML-based web services
SOAP libraries
JSON-based web services
Web API design approaches
REST
GraphQL
API description languages
OpenAPI
AsyncAPI
Remote procedure calls
Apache Thrift
RPC with RabbitMQ
gRPC
Summary
Questions
Further reading
Containers
Technical requirements
Reintroducing containers
Exploring the container types
The parallel rise of microservices and containers
Choosing when to use containers
The benefits of containers
The disadvantages of containers
Building container images
Container images explained
Building container images with Dockerfiles
Naming and distributing images
Compiled applications and containers
Targeting multiple architectures
Alternative tools to build application containers
Buildah
ansible-bender
Integrating containers with CMake
Configuring the Dockerfile with CMake
Building container images with CMake
Practical runtime considerations for containers
Runtime libraries inside containers
Alternative container runtimes
Understanding container orchestration
Self-hosted solutions
Kubernetes
Docker Swarm
Nomad
OpenShift
Managed services
AWS ECS
AWS Fargate
Azure Service Fabric
Summary
Questions
Further reading
Observability
Technical requirements
Logging
Logging with microservices
Logging in C++ with spdlog
Unified logging
Logstash
Filebeat
Fluentd
Fluent Bit
Vector
Log aggregation
Elasticsearch
Loki
Log visualization
Kibana
Grafana
Monitoring
Implementing application metrics
Monitoring health checks
Tracing
Jaeger and OpenTracing
Zipkin
OpenTelemetry
Summary
Questions
Further reading
Cloud-Native Design
Technical requirements
Understanding cloud-native
Cloud Native Computing Foundation
Cloud as an operating system
Load balancing
Reverse and forward proxies
Service discovery
Using Kubernetes to orchestrate cloud-native workloads
Kubernetes structure
Control plane
Worker nodes
Possible approaches to deploying Kubernetes
Understanding the Kubernetes concepts
Managing Kubernetes declaratively
Kubernetes networking
Container-to-container communication
Pod-to-Pod communication
Pod-to-Service communication
External-to-internal communication
What to consider before adopting Kubernetes
Observability in cloud-native distributed systems
Instrumenting an application with OpenTelemetry
Connecting services with a service mesh
Introducing a service mesh
Service mesh solutions
Istio
Linkerd
Consul service mesh
Going GitOps
The principles of GitOps
Declarative description
System state versioned in Git
Auditability
Integrated with established components
Configuration drift prevention
The benefits of GitOps
Increased productivity
Better developer experience
Higher stability and reliability
Improved security
GitOps tools
FluxCD
ArgoCD
Jenkins X
Summary
Questions
Further reading
Unlock Your Book’s Exclusive Benefits
How to unlock these benefits in three easy steps
Step 1
Step 2
Step 3
Need help?
Assessments
Chapter 1
Chapter 2
Chapter 3
Chapter 4
Chapter 5
Chapter 6
Chapter 7
Chapter 8
Chapter 9
Chapter 10
Chapter 11
Chapter 12
Chapter 13
Chapter 14
Chapter 15
Chapter 16
Chapter 17
Chapter 18
Other Books You May Enjoy
Subscribe to Deep Engineering
Index