Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Book Overview & Buying Software Architecture with C++
  • Table Of Contents Toc
Software Architecture with C++

Software Architecture with C++ - Second Edition

By : Andrey Gavrilin, Adrian Ostrowski, Piotr Gaczkowski
5 (1)
close
close
Software Architecture with C++

Software Architecture with C++

5 (1)
By: Andrey Gavrilin, Adrian Ostrowski, Piotr Gaczkowski

Overview of this book

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
Table of Contents (27 chapters)
close
close
1
Concepts and Components of Software Architecture
5
The Design and Development of C++ Software
12
Architectural Quality Attributes
17
Cloud-Native Design Principles
26
Index

Coupling and cohesion

Low cohesion and high coupling are usually associated with software that’s difficult to test, reuse, maintain, or even understand, so it lacks many of the quality attributes usually desired to have in software.

Figure 1.5: Coupling versus cohesion

Those terms often go together because one trait often influences the other, regardless of whether the unit we talk about is a function, class, module, library, service, or even a whole system. To give an example, usually, monoliths are highly coupled and have low cohesion, while distributed services tend to be at the other end of the spectrum.

Coupling

Coupling is a measure of how strongly one software unit depends on other units. A unit with high coupling relies on many other units. The lower the coupling, the better.

An example of tightly coupled classes is the first implementation of the NotificationSystem and notifier classes while discussing the dependency inversion topic. This principle reduces the degree of direct knowledge of modules about each other to reduce their coupling. Let’s see what would happen if we were to add yet another notifier type:

class ChatNotifier {
public:
    void sendMessage(const std::string &message) {
        std::cout << "Chat channel: " << message << std::endl;
    }
};
class NotificationSystem {
public:
    void notify(const std::string &message) {
        sms_.sendSMS(message);
        email_.sendEmail(message);
        chat_.sendMessage(message);
    }
private:
    SMSNotifier sms_;
    EMailNotifier email_;
    ChatNotifier chat_;
};

It looks like instead of just adding the ChatNotifier class, we had to modify the public interface of the NotificationSystem class. This means they’re tightly coupled, and that this implementation of the NotificationSystem class actually breaks the OCP. For comparison, let’s now see how the same modification would be applied to the implementation using dependency inversion:

class ChatNotifier {
public:
    void notify(const std::string &message) { sendMessage(message);  }
private:
    void sendMessage(const std::string &message) {
        std::cout << "Chat channel: " << message << std::endl;
    }
};

No changes to the NotificationSystem class were required, so now the classes are loosely coupled. All we needed to do was add the ChatNotifier class. Structuring our code this way allows for smaller rebuilds, faster development, and easier testing, all with less code that’s easier to maintain. To use our new class, we only need to modify the calling code:

using MyNotificationSystem =
    NotificationSystem<SMSNotifier, EMailNotifier, ChatNotifier>;
auto sn = SMSNotifier{};
auto en = EMailNotifier{};
auto cn = ChatNotifier{};
auto ns = MyNotificationSystem{{sn, en, cn}};
ns.notify("Azabeth Burns");

This shows coupling on a class level. On a larger scale, for instance, if you’re having a microservice architecture, a common pattern is to have multiple services use a shared database and communicate via this database. This causes high coupling between those services, as you cannot freely modify the database schema without affecting all the microservices that use it. A better option is to have a database per service, wherein the low coupling can be achieved by introducing techniques such as message queueing, where services communicate by sending messages to a queue instead of calling each other. The services wouldn’t then depend on each other directly, but just on the message format. However, having one database per service can be extremely expensive. The shared instance pattern is a compromise pattern that helps solve the issue. Here, services must request data from other services via the API or other techniques because services must access only their parts of the data to loosen coupling.

Figure 1.6: Microservices database design patterns

Let’s now move on to cohesion.

Cohesion

Cohesion is a measure of how strongly software elements belong together. In a system, the functionality offered by components in a module should be strongly related to make it highly cohesive.

Consider the following example. It may seem trivial, but posting real-life scenarios, often hundreds if not thousands of lines long, would be impractical:

class CachingProcessor {
public:
    Result process(WorkItem work);
    Results processBatch(WorkBatch batch);
    void addListener(const Listener &listener);
    void removeListener(const Listener &listener);
private:
    void addToCache(const WorkItem &work, const Result &result);
    void findInCache(const WorkItem &work);
    void limitCacheSize(std::size_t size);
    void notifyListeners(const Result &result);
    // ...
};

We can see that our processor does three types of work and, therefore, violates SRP: the actual work, the caching of the results, and managing listeners. A common way to increase cohesion in such scenarios is to extract a class or even multiple classes:

class WorkResultsCache {
public:
    void addToCache(const WorkItem &work, const Result &result);
    void findInCache(const WorkItem &work);
    void limitCacheSize(std::size_t size);
private:
    // ...
};
class ResultNotifier {
public:
    void addListener(const Listener &listener);
    void removeListener(const Listener &listener);
    void notify(const Result &result);
private:
    // ...
};
class CachingProcessor {
public:
    explicit CachingProcessor(ResultNotifier &notifier);
    Result process(WorkItem work);
    Results processBatch(WorkBatch batch);
private:
    WorkResultsCache cache_;
    ResultNotifier notifier_;
    // ...
};

Now, each part is done by a separate, highly cohesive entity. Reusing them is now possible without much hassle. Even making them a template class should require little work. Last but not least, testing such classes should be easier as well.

Putting this on a component or system level is straightforward—each component, service, and system you design should be concise and focus on doing one thing and doing it right. This concludes our introductory chapter. Let’s now summarize what we’ve learned.

CONTINUE READING
83
Tech Concepts
36
Programming languages
73
Tech Tools
Icon Unlimited access to the largest independent learning library in tech of over 8,000 expert-authored tech books and videos.
Icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Icon 50+ new titles added per month and exclusive early access to books as they are being written.
Software Architecture with C++
notes
bookmark Notes and Bookmarks search Search in title playlist Add to playlist download Download options font-size Font size

Change the font size

margin-width Margin width

Change margin width

day-mode Day/Sepia/Night Modes

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Confirmation

Modal Close icon
claim successful

Buy this book with your credits?

Modal Close icon
Are you sure you want to buy this book with one of your credits?
Close
YES, BUY

Submit Your Feedback

Modal Close icon
Modal Close icon
Modal Close icon