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. Modern C++ allows developers 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. The book will then explain what software architecture is and help you explore 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 who are looking to become software architects or are interested in developing enterprise-grade applications.
Author(s): Adrian Ostrowski, Piotr Gaczkowski
Edition: 1
Publisher: Packt
Year: 2021
Language: English
Pages: 522
Tags: c++17 c++20 uml c++
Cover
Title Page
Copyrights
Dedication
Contributors
Table of Contents
Preface
Section 1: Concepts and Components of Software Architecture
Chapter 1: Importance of Software Architecture and Principles of Great Design
Technical requirements
Understanding software architecture
Different ways to look at architecture
Learning the importance of proper architecture
Software decay
Accidental architecture
Exploring 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
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
Chapter 2: Architectural Styles
Technical requirements
Deciding between stateful and stateless approaches
Stateless and stateful services
Understanding monoliths—why they should be avoided, and recognizing exceptions
Understanding services and microservices
Microservices
Benefits and disadvantages of microservices
Characteristics of microservices
Microservices and other architectural styles
Scaling microservices
Transitioning to microservices
Exploring event-based architecture
Common event-based topologies
Event sourcing
Understanding layered architecture
Backends for Frontends
Learning module-based architecture
Summary
Questions
Further reading
Chapter 3: Functional and Nonfunctional Requirements
Technical requirements documentation from sources, you must have
Understanding the types of requirements
Functional requirements
Nonfunctional requirements
Quality attributes
Constraints
Recognizing architecturally significant requirements
Indicators of architectural significance
Hindrances in recognizing ASRs and how to deal with them
Gathering requirements from various sources
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 nonfunctional requirements
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
Section 2: The Design and Development of C++ Software
Chapter 4: Architectural and System Design
Technical requirements
Understanding the peculiarities of distributed systems
Different service models and when to use them
On-premises model
Infrastructure as a Service (IaaS) model
Platform as a Service (PaaS) model
Software as a Service (SaaS) model
Function as a Service (FaaS) 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
Sagas and compensating transactions
Choreography-based sagas
Orchestration-based sagas
Making your system fault tolerant and available
Calculating your system's availability
Building fault-tolerant systems
Redundancy
Leader election
Consensus
Replication
Master-slave replication
Multi-master replication
Queue-based load leveling
Back pressure
Detecting faults
Sidecar design pattern
Heartbeat mechanism
Leaky bucket counter
Minimizing the impact of faults
Retrying the call
Avoiding cascading failures
Circuit breaker
Bulkhead
Geodes
Integrating your system
Pipes and filters pattern
Competing consumers
Transitioning from legacy systems
Anti-corruption layer
Strangler pattern
Achieving performance at scale
CQRS and event sourcing
Command-query responsibility segregation
Command-query separation
Event sourcing
Caching
Updating caches
Write-through approach
Write-behind approach
Cache-aside
Deploying your system
The sidecar pattern
Deploying a service with tracing and a reverse proxy using Envoy
Zero-downtime deployments
Blue-green deployments
Canary releases
External configuration store
Managing your APIs
API gateways
Summary
Questions
Further reading
Chapter 5: Leveraging C++ Language Features
Technical requirements
Designing great APIs
Leveraging RAII
Specifying the interfaces of containers in C++
Using pointers in interfaces
Specifying preconditions and postconditions
Leveraging inline namespaces
Leveraging std::optional
Optional function parameters
Optional function return values
Optional class members
Writing declarative code
Showcasing a featured items gallery
Introducing standard ranges
Reducing memory overhead and increasing performance using ranges
Moving computations at compile time
Helping the compiler help you by using const
Leveraging the power of safe types
Constraining template parameters
Writing modular C++
Summary
Questions
Further reading
Chapter 6: Design Patterns and C++
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
Policy-based design idiom
Curiously recurring template pattern
Knowing when to use dynamic versus static polymorphism
Implementing static polymorphism
Interlude – using type erasure
Creating objects
Using factories
Using factory methods
Using factory functions
Choosing the return type of a factory
Using factory classes
Using builders
Building with composites and prototypes
Tracking state and visiting objects in C++
Dealing with memory efficiently
Reducing dynamic allocations using SSO/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
Chapter 7: 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
Introducing 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
Using the Conan package manager
Preparing Conan profiles
Specifying Conan dependencies
Installing Conan dependencies
Using Conan targets from CMake
Adding tests
Reusing quality code
Installing
Exporting
Using CPack
Packaging using Conan
Creating the conanfile.py script
Testing our Conan package
Adding Conan packaging code to our CMakeLists
Summary
Questions
Further reading
Section 3: Architectural Quality Attributes
Chapter 8: 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
Introducing testing frameworks
GTest examples
Catch2 examples
CppUnit examples
Doctest examples
Testing compile-time code
Understanding mocks and fakes
Different test doubles
Other uses for test doubles
Writing test doubles
GoogleMock example
Trompeloeil example
Test-driven class design
When tests and class design clash
Defensive programming
The boring refrain – write your tests first
Automating tests for continuous integration/continuous deployment
Testing the infrastructure
Testing with Serverspec
Testing with Testinfra
Testing with Goss
Summary
Questions
Further reading
Chapter 9: Continuous Integration and Continuous Deployment
Technical requirements
Understanding CI
Release early, release often
Merits of CI
Gating mechanism
Implementing the pipeline with GitLab
Reviewing code changes
Automated gating mechanisms
Code review – the manual gating mechanism
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 components to create deployment code
Building deployment code
Building a CD pipeline
Continuous deployment and continuous delivery
Building an example CD pipeline
Using immutable infrastructure
What is immutable infrastructure?
The benefits of immutable infrastructure
Building instance images with Packer
Orchestrating the infrastructure with Terraform
Summary
Questions
Further reading
Chapter 10: Security in Code and Deployment
Technical requirements
Checking the code security
Security-conscious design
Making interfaces easy to use and hard to misuse
Enabling automatic resource management
Drawbacks of concurrency and how to deal with it
Secure coding, the guidelines, and GSL
Defensive coding, validating everything
The most common vulnerabilities
Checking whether the dependencies are secure
Common Vulnerabilities and Exposures
Automated scanners
Automated dependency upgrade management
Hardening your code
Security-oriented memory allocator
Automated checks
Compiler warnings
Static analysis
Dynamic analysis
Valgrind and Application Verifier
Sanitizers
Fuzz-testing
Process isolation and sandboxing
Hardening your environment
Static versus dynamic linking
Address space layout randomization
DevSecOps
Summary
Questions
Further reading
Chapter 11: Performance
Technical requirements
Measuring performance
Performing accurate and meaningful measurements
Leveraging different types of measuring tools
Using microbenchmarks
Setting up Google Benchmark
Writing your first microbenchmark
Passing arbitrary arguments to a microbenchmark
Passing numeric arguments to a microbenchmark
Generating the passed arguments programmatically
Choosing what to microbenchmark and optimize
Creating performance tests using benchmarks
Profiling
Choosing the type of profiler to use
Preparing the profiler and processing the results
Analyzing the results
Tracing
Correlation IDs
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
Understanding the differences between threads and processes
Using the standard parallel algorithms
Parallelizing computations using OpenMP and MPI
Using coroutines
Distinguishing between cppcoro utilities
Looking under the hood of awaitables and coroutines
Summary
Questions
Further reading
Section 4: Cloud-Native Design Principles
Chapter 12: Service-Oriented Architecture
Technical requirements
Understanding Service-Oriented Arcitecture
Implementation approaches
Enterprise Service Bus
Web services
Benefits and disadvantages of web services
Messaging and streaming
Message queues
Message brokers
Cloud computing
Microservices
Benefits of Service-Oriented Architecture
Challenges with SOA
Adopting messaging principles
Low-overhead messaging systems
MQTT
ZeroMQ
Brokered messaging systems
Using web services
Tools for debugging web services
XML-based web services
XML-RPC
Relationship to SOAP
SOAP
WSDL
UDDI
SOAP libraries
JSON-based web services
JSON-RPC
REpresentational State Transfer (REST)
Description languages
OpenAPI
RAML
API Blueprint
RSDL
Hypermedia as the Engine of Application State
REST in C++
GraphQL
Leveraging managed services and cloud providers
Cloud computing as an extension of SOA
Using API calls directly
Using API calls through a CLI tool
Using third-party tools that interact with the Cloud API
Accessing the cloud API
Using the cloud CLI
Using tools that interact with the Cloud API
Cloud-native architecture
Summary
Questions
Further reading
Chapter 13: Designing Microservices
Technical requirements
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
Decomposition by business capability
Decomposition by subdomain
Database per service pattern
Deployment strategies
Single service per host
Multiple services per host
Observability patterns
Log aggregation
Application metrics
Distributed tracing
Health check APIs
Building microservices
Outsourcing memory management
Memcached
Redis
Which in-memory cache is better?
Outsourcing storage
Outsourcing computing
Observing microservices
Logging
Logging with microservices
Logging in C++ with spdlog
Unified logging layer
Logstash
Filebeat
Fluentd
Fluent Bit
Vector
Log aggregation
Elasticsearch
Loki
Log visualization
Kibana
Grafana
Monitoring
Tracing
OpenTracing
Jaeger
OpenZipkin
Integrated observability solutions
Connecting microservices
Application programming interfaces (APIs)
Remote procedure calls
Apache Thrift
gRPC
Scaling microservices
Scaling a single service per host deployment
Scaling multiple services per host deployment
Summary
Questions
Further reading
Chapter 14: Containers
Technical requirements
Reintroducing containers
Exploring the container types
The rise of microservices
Choosing when to use containers
The benefits of containers
The disadvantages of containers
Building containers
Container images explained
Using Dockerfiles to build an application
Naming and distributing images
Compiled applications and containers
Targeting multiple architectures with manifests
Alternative ways to build application containers
Buildah
Ansible-bender
Others
Integrating containers with CMake
Configuring the Dockerfile with CMake
Integrating containers with CMake
Testing and integrating 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
Chapter 15: Cloud-Native Design
Technical requirements
Understanding cloud-native
Cloud-Native Computing Foundation
Cloud as an operating system
Load balancing and service discovery
Reverse 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
Declarative approach
Kubernetes networking
Container-to-container communication
Pod-to-pod communication
Pod-to-service communication
External-to-internal communication
When is using Kubernetes a good idea?
Observability in distributed systems
How tracing differs from logging
Choosing a tracing solution
Jaeger and OpenTracing
Zipkin
Instrumenting an application with OpenTracing
Connecting services with a service mesh
Introducing a service mesh
Service mesh solutions
Istio
Envoy
Linkerd
Consul service mesh
Going GitOps
The principles of GitOps
Declarative description
The system's state versioned in Git
Auditable
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
Appendix A
Assessments
About Packt
Other Books You May Enjoy
Index