Rethinking Layered Architecture: Lessons from Evolving the Coco System

The article chronicles the evolution of Coco's layered architecture, detailing the shift from a monolithic Clean‑Architecture‑inspired design to a modular, plugin‑driven structure, and discusses the trade‑offs of infrastructure layering, code reuse, and future adaptability.

phodal
phodal
phodal
Rethinking Layered Architecture: Lessons from Evolving the Coco System

When optimizing Coco's layered architecture, the author faced many decision dilemmas and chose to delay choices to better understand the current system, effectively walking on the edge of risk to maximize benefit.

Design Foundations

Architecture decisions are guided by three factors: past experience, present requirements, and future direction. Because these factors constantly evolve, a design that satisfies every historical period is impossible; the present becomes the past, and the future becomes the present.

Original Monolithic Structure

Initially, Coco adopted a Clean‑Architecture‑like layering (without a Cargo module) consisting of four top‑level directories:

app – the use‑case layer.

bin – the controller layer that builds an executable in Rust.

infrastructure – services such as Git interaction and file‑system access.

domain – business entities and domain models.

Within domain, the project further split into sub‑domains like cloc, git, framework, architecture, etc.

Although the design resembled Clean Architecture, key principles such as dependency inversion were not fully applied, leading to added complexity and limited promotion potential in open‑source contexts.

Monolith Evaluation

The structure adds architectural complexity and requires continuous knowledge transfer.

Refactoring can later convert the monolith into a cleaner architecture; the author treats the current work as a learning exercise.

For a single‑application monolith, the design is acceptable because:

It is not overly complex and helps developers locate code.

It can evolve into a full Clean Architecture.

Modules can be further split by business domain.

Modularization for Code Reuse

To share code across multiple systems, independent modules such as framework and psa were extracted. These modules can be used locally without versioning concerns, and later published remotely when ready.

Typical modularization places each module as a sibling directory to the source code:

├── framework
├── psa
├── src
│   ├── app
│   ├── bin
│   ├── domain
│   ├── infrastructure
│   └── lib.rs

If a module’s dependencies are fully decoupled, it can be released as an independent application.

Copy‑over Reuse

When a module is tiny, copying the code instead of reusing it can simplify dependency management.

Plugin‑Based Architecture Evolution

To flexibly extend functionality and reduce binary size, a plugins directory was introduced, containing modules like coco_xxxx. A plugin_manager was created to load these plugins, although later it was deemed unnecessary as a separate module.

├── framework
├── plugin_manager
├── plugins
│   ├── coco_container
│   ├── coco_pipeline
│   ├── coco_struct
│   └── coco_swagger
├── psa
├── src
│   ├── app
│   ├── bin
│   ├── domain
│   ├── infrastructure
│   └── lib.rs

The design remains usable and does not introduce major issues.

Future Directions

Recognizing missing considerations, the author proposes extracting a dedicated core module to expose essential code to plugins and applications, leading to a core_model implementation that avoids bundling non‑core logic.

For shared infrastructure, two options were evaluated:

Extract the existing infrastructure into a top‑level module.

Introduce a double‑layered infrastructure where only common code is extracted as an independent module.

The author favors the double‑layer approach to prevent code duplication and binary bloat, citing examples of overly nested common packages.

Conclusion

Even small projects undergo continuous architectural evolution; uncontrolled growth can lead to unwieldy systems. The author hints at developing a "layered architecture fitness function" to rate the suitability of a system's layering based on dependency analysis.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

modularizationsoftware designPlugin System
phodal
Written by

phodal

A prolific open-source contributor who constantly starts new projects. Passionate about sharing software development insights to help developers improve their KPIs. Currently active in IDEs, graphics engines, and compiler technologies.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.