DEV Community

Gaspard Kirira
Gaspard Kirira

Posted on

# How Should C++ Application Modules Scale Across Repositories and Teams?

How Should C++ Application Modules Scale Across Repositories and Teams?

I’m currently thinking about the next evolution of Vix Application Modules, and I would like feedback from developers who have worked on large C++ systems.

Vix modules were created to make ownership visible inside an application.

Instead of placing every controller, service, model, test, migration, and dependency inside the same large source tree, a feature can own its own module:

modules/
  auth/
  projects/
  billing/
  notifications/
Enter fullscreen mode Exit fullscreen mode

Each module can have:

  • its own public headers;
  • private implementation files;
  • tests;
  • migrations;
  • CMake target;
  • registry dependencies;
  • routes;
  • explicit dependencies on other modules.

For example:

[module.projects]
enabled = true
path = "modules/projects"
kind = "backend"
depends = [
  "auth",
]
Enter fullscreen mode Exit fullscreen mode

This makes the internal architecture visible from the application manifest.

A developer can understand that projects depends on auth without searching through many source files or discovering the relationship from compiler errors.

The current model works well when the modules belong to the same application and are developed inside the same repository.

But what happens when the application becomes much larger?

The multi-team problem

Imagine a large real-time platform containing different native places:

cities
rivers
forests
homes
public spaces
transport
identity
moderation
Enter fullscreen mode Exit fullscreen mode

Each place or system could become a significant product area.

One team may work on cities.

Another team may work on rivers and natural spaces.

Another may own identity and permissions.

Another may be responsible for real-time presence.

At some point, keeping everything in one repository may no longer be the best development model.

The application may need to support:

  • multiple repositories;
  • independent teams;
  • different release schedules;
  • separately versioned modules;
  • shared contracts;
  • local development without the complete platform;
  • validation before integration.

This creates an important question:

How should a team develop and test one application module independently before integrating it into the complete application?

A module should not require the entire application

Suppose a team owns a river module.

That team should not need to download, configure, build, and run the entire platform just to test:

  • river state;
  • movement rules;
  • interactions;
  • permissions;
  • events;
  • persistence;
  • WebSocket behavior.

The team should be able to work inside the module repository and run something like:

vix modules check
vix modules test
vix modules run
Enter fullscreen mode Exit fullscreen mode

The exact commands are not decided yet, but the expected workflow is important.

A module should have enough information to describe:

  • what it exports;
  • what it requires;
  • which contracts it implements;
  • which modules it depends on;
  • which external services it expects;
  • how it should be tested;
  • how it can run in isolation.

A generated module host

One possible direction is a minimal generated host application.

A developer working on the river module could run:

vix modules run river
Enter fullscreen mode Exit fullscreen mode

Vix could then generate a temporary application containing only:

  • the module itself;
  • its declared dependencies;
  • mocked or local infrastructure;
  • a minimal HTTP or WebSocket runtime;
  • test configuration;
  • development resources.

Conceptually:

River module
    ↓
Generated development host
    ↓
Minimal Vix runtime
    ↓
Local tests and manual validation
Enter fullscreen mode Exit fullscreen mode

This would let the module behave like part of the real application without requiring the complete production system.

The development host would not replace integration tests.

Its purpose would be to give each team a fast and deterministic environment for daily work.

Explicit contracts between modules

Dependencies between repositories cannot depend only on header files and CMake target names.

A module may need to declare the contract it expects from another module.

For example, a city module may need an identity provider:

[requires.identity]
version = "^2.0"
contract = "vix.identity.session"
Enter fullscreen mode Exit fullscreen mode

The identity implementation could be provided by another repository.

During local development, the city team could use:

  • a mock implementation;
  • a lightweight reference implementation;
  • a published development version;
  • the real module when available.

This creates another question:

Should Vix modules depend on concrete modules, stable interfaces, or versioned contracts?

Concrete dependencies are simpler.

Contracts provide more independence.

But contracts also introduce additional complexity, compatibility rules, tooling, and documentation requirements.

Versioned application modules

If modules live in separate repositories, they need stable identities and versions.

A root application could eventually declare modules like this:

[module.identity]
source = "softadastra/identity"
version = "^2.1"
kind = "backend"

[module.river]
source = "softadastra/river"
version = "^1.4"
depends = [
  "identity",
]
Enter fullscreen mode Exit fullscreen mode

Vix could resolve the module repositories, verify their metadata, validate their compatibility, and connect their CMake targets.

But application modules are not necessarily normal libraries.

They may own:

  • routes;
  • database migrations;
  • runtime hooks;
  • WebSocket handlers;
  • scheduled jobs;
  • resources;
  • configuration;
  • application state.

This means installing an application module is more complicated than linking a package.

The system must understand what the module contributes to the application.

Testing without integration

There are several levels of testing that may be needed.

Module tests

Tests owned entirely by the module:

modules/river/tests/
Enter fullscreen mode Exit fullscreen mode

These validate the public API and internal behavior of the feature.

Contract tests

Tests that verify whether a dependency provides the behavior expected by the module.

For example:

River expects Identity Contract v2
Enter fullscreen mode Exit fullscreen mode

The identity implementation could run the same contract test suite before publishing a new version.

Host tests

Tests that run the module inside a minimal generated Vix application.

This could validate:

  • route registration;
  • WebSocket registration;
  • configuration;
  • migrations;
  • runtime startup;
  • dependency injection;
  • event handling.

Integration tests

Tests that run several real modules together.

For example:

identity + city + river + presence
Enter fullscreen mode Exit fullscreen mode

Full application tests

Tests that validate the complete product.

The challenge is making these levels clear without creating a complicated workflow that developers avoid using.

Local replacements

A multi-repository workflow should still allow teams to test changes together before publishing them.

The root application may use published module versions:

[module.river]
source = "softadastra/river"
version = "1.4.0"
Enter fullscreen mode Exit fullscreen mode

But a developer could temporarily replace that module with a local checkout:

[module.river]
path = "../river"
Enter fullscreen mode Exit fullscreen mode

Or through a command:

vix modules override river ../river
Enter fullscreen mode Exit fullscreen mode

The application could then build against the local module without changing the shared production configuration.

This would be useful when two teams are collaborating on a contract change.

Compatibility checks before integration

Compilation alone may not be enough to determine whether several application modules are compatible.

Vix could potentially validate:

  • dependency versions;
  • contract versions;
  • duplicate route ownership;
  • conflicting migrations;
  • runtime ownership;
  • incompatible configuration requirements;
  • duplicate event names;
  • dependency cycles;
  • unsupported application versions.

A command such as:

vix modules check --integration
Enter fullscreen mode Exit fullscreen mode

could validate the complete module graph before the application is built.

The goal would be to report architectural problems directly instead of letting them appear later as difficult compiler, linker, migration, or runtime failures.

Questions for the C++ community

I’m still exploring the right model, and I do not want to design this only from assumptions.

I would especially like feedback from developers working with:

  • large C++ monorepos;
  • multiple C++ repositories;
  • plugin systems;
  • game engines;
  • embedded platforms;
  • real-time applications;
  • distributed engineering teams;
  • modular backends.

Here are the main questions:

  1. Should an application module be independently buildable outside the main application?

  2. Should Vix generate a minimal host application for local module development?

  3. Should dependencies point to concrete modules or versioned contracts?

  4. How should mocks and reference implementations be shared?

  5. Should modules be published like normal packages, or should application modules have a separate distribution model?

  6. How should database migrations owned by separate modules be coordinated?

  7. How should multiple teams validate compatibility before integration?

  8. What information should a module manifest contain?

  9. Which parts of this workflow would genuinely help your team?

  10. Which parts would introduce unnecessary complexity?

The goal

The goal is not to transform every feature into a microservice.

A Vix application should still be able to remain one application and one executable.

The goal is to preserve that deployment simplicity while allowing the codebase and the teams behind it to scale independently.

In other words:

One application at runtime, but clear ownership and independent development before integration.

Vix Application Modules already provide local feature boundaries, public and private APIs, tests, dependencies, metadata, routes, and generated registration.

The next question is whether these boundaries should grow beyond one repository.

I would love to hear how you would design this workflow.

Documentation:

https://docs.vixcpp.com/app-modules/

Top comments (1)

Collapse
 
matthew_faithfull profile image
Matthew Faithfull

I've only really got a decent understanding of modular backends but that lets me answer the first 3 questions with some certainty despite not being a Vix user.

Should an application module be independently buildable outside the main application?
Yes.

Should Vix generate a minimal host application for local module development?
Yes or provide the hosting itself as a module.

Should dependencies point to concrete modules or versioned contracts?
Versioned contracts if possible. Having worked on distributed systems with versioned Kafka messaging contracts I know this can work even though it's difficult. You'll likely need meta data for forward/backward compatibility (i.e. change by extension doesn't break things) in practice or large systems will become too rigid. This can easily get too complex though with levels of deprecation and obseleting.