Domain-Driven Design in C++: Architecture for Complex Projects

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why DDD is Not About Boring Diagrams, But About Living Architecture

\n

When a C++ project outgrows the size of a "toy" application, developers often encounter chaos: business logic is smeared across controllers, the database dictates class structure, and newcomers are afraid to touch legacy code. Domain-Driven Design (DDD) is not a set of abstract rules, but a practical tool that helps tame complexity. In this article, we'll break down how to implement DDD in C++ using concrete code examples, and why it will save your project from becoming a "big ball of mud."

\n\n

What is DDD and Why Does a C++ Developer Need It?

\n

Domain-Driven Design is an approach where the primary focus is on the domain model, not on technical details. In C++, this is especially relevant: the language provides powerful tools (templates, RAII, smart pointers), but without proper architecture, the project turns into a monolith where changing one feature breaks everything.

\n

DDD offers two levels:

\n
    \n
  • Strategic Modeling: dividing the system into contexts (Bounded Contexts) and configuring their interaction.
  • \n
  • Tactical Modeling: building blocks — Entity, Value Object, Aggregate, Repository, Domain Service.
  • \n
\n

We will focus on tactical modeling, as it provides concrete patterns for C++ code.

\n\n

Layered Architecture: The Foundation of DDD in C++

\n

DDD implies a clear separation into layers. A typical structure for a C++ project looks like this:

\n
    \n
  • Domain Layer (core): entities, value objects, aggregates, domain services. No dependencies on frameworks or external libraries.
  • \n
  • Application Layer: use cases, orchestration. Depends only on Domain.
  • \n
  • Infrastructure Layer: implementation of repositories (SQL, Redis, files), work with external APIs. Implements interfaces from Domain.
  • \n
  • Presentation Layer: user interface (console, REST API, GUI).
  • \n
\n

This is not a strict rule, but a recommendation. The main point is that dependencies point inward: outer layers can depend on inner layers, but not vice versa.

\n\n

Example: How Not to Do It

\n
// ❌ Bad: business logic mixed with SQL\nclass UserController {\n    void createUser(const std::string& name) {\n        // SQL query right here\n        db.execute("INSERT INTO users (name) VALUES (?)", name);\n        // Validation logic also here\n        if (name.empty()) throw std::runtime_error("Name required");\n    }\n};\n
\n\n

Example: DDD Style

\n
// ✅ Good: clear separation\nclass User : public Entity<User, int> {\n    std::string name_;\npublic:\n    explicit User(int id, std::string name) : Entity(id), name_(std::move(name)) {\n        if (name_.empty()) throw std::invalid_argument("User name cannot be empty");\n    }\n    const std::string& name() const { return name_; }\n};\n\n// Repository interface (Domain)\nclass IUserRepository {\npublic:\n    virtual ~IUserRepository() = default;\n    virtual void save(const User& user) = 0;\n};\n\n// Implementation (Infrastructure)\nclass SqlUserRepository : public IUserRepository {\n    void save(const User& user) override {\n        // Only SQL, no business logic\n    }\n};\n
\n\n

DDD Building Blocks in C++

\n\n

Entity

\n

This is an object with a unique identifier that persists over time. In C++, it's convenient to create a base template class:

\n
template<typename T, typename IdType>\nclass Entity {\n    IdType id_;\npublic:\n    explicit Entity(IdType id) : id_(std::move(id)) {}\n    IdType id() const { return id_; }\n    bool operator==(const Entity& other) const { return id_ == other.id_; }\n                

Blogs

Book Recommendations