---
layout: default
title: "Modular Architectures: 10 Principles for Building Evolutionary Systems"
description: "A formal framework for building systems that evolve sustainably"
authors:
- Waldemar Neto
- William Calderipe
affiliation: TechLeads.club
date: 2025-10-13
version: 1.0
license: CC BY 4.0
contact: waldemarnt@gmail.com
---
# Modular Architectures: 10 Principles for Building Evolutionary Systems
**Waldemar Neto¹, William Calderipe¹**
¹TechLeads.club
---
## Abstract
The software industry faces a fundamental granularity dilemma: traditional monoliths suffer from high local complexity and limited scalability, while microservices introduce premature operational complexity that often outweighs their benefits. Recent empirical evidence, including research conducted by Google teams (Ghemawat et al., 2023), demonstrates that microservices generate performance loss, reduced visibility, management difficulties, API freezing, and development slowdown. This work presents **Modular Architectures** as a formal framework positioned between these extremes, based on conscious separation between logical boundaries (modules) and physical boundaries (deployment). We propose **10 fundamental principles** that form a cohesive framework for building systems that evolve sustainably: Well-Defined Boundaries, Composability, Independence, State Isolation, Explicit Communication, Replaceability, Deployment Independence, Individual Scale, Observability, and Fail Independence. Our main contribution is the formalization of these principles as an architectural model that enables gradual evolution of granularity, reducing cognitive load and maximizing delivery velocity without incurring premature complexity.
**Keywords:** software architecture, modularity, monorepos, microservices, Domain-Driven Design, software granularity
---
## Table of Contents
1. [Introduction and Contextualization](#1-introduction-and-contextualization)
2. [State of the Art and Limitations](#2-state-of-the-art-and-limitations)
3. [Modular Architectures - Formal Definition](#3-modular-architectures---formal-definition)
4. [The 10 Fundamental Principles](#4-the-10-fundamental-principles)
5. [Implementation Patterns](#5-implementation-patterns)
6. [Organizational Governance](#6-organizational-governance)
7. [Comparative Analysis and Trade-offs](#7-comparative-analysis-and-trade-offs)
8. [Related Work](#8-related-work)
9. [Conclusion](#9-conclusion)
10. [References](#references)
---
## 1. Introduction and Contextualization
The evolution of software architectures in recent decades reflects profound changes in both technological infrastructure and organizational practices. From the **traditional monoliths** of the 1990s-2000s, through **Service-Oriented Architecture (SOA)** in the 2000s, to the **microservices** that dominated the 2010s, each paradigm brought promises of better organization, scalability, and maintainability.
### 1.1 The Granularity Problem in Software
**Granularity** defines the size and cohesion of units that compose a system — whether functions, classes, modules, or services. This is one of the most critical architectural decisions, as it directly impacts:
- **Technical complexity**: amount of abstractions and integration points
- **Cognitive load**: volume of information developers need to manage
- **Evolution velocity**: ease of implementing changes
We can classify granularity into three levels:
- **Coarse-grained**: larger units with multiple grouped responsibilities (e.g., traditional monoliths)
- **Medium-grained**: cohesive modules representing business domains (e.g., modular architectures)
- **Fine-grained**: small, specific units with independent deployment (e.g., microservices)
### 1.2 Paradigm Shift: Cloud and Modern Tooling
The advent of **cloud computing**, **containers**, **orchestration (Kubernetes)**, and **serverless** fundamentally changed architectural possibilities. For the first time, it became viable to:
- Modularize systems and scale each part independently
- Deploy selectively without recompiling the entire system
- Manage complex dependencies with sophisticated tools (Nx, Bazel, Turborepo)
Paradoxically, this flexibility created a new challenge: **complexity migrated from code to distributed architecture**. As observed by Khononov (2023) in "Balancing Coupling in Software Design," complexity in modern systems emerges primarily from **communication between components**, not from the internal code of each one.
### 1.3 Local vs Global Complexity
In traditional monolithic systems, **complexity was local**: infrastructure changes (database, frameworks) impacted the entire system. The response was rigid abstraction layers and patterns like Hexagonal Architecture and Clean Architecture.
In modern distributed systems, **complexity is global**: the challenge is not protecting the domain from the ORM, but ensuring clear contracts between services, managing eventual consistency, observability, and deployment orchestration.
### 1.4 Objective of This Work
This paper proposes **Modular Architectures** as an intermediate framework that:
1. Maintains the **visibility and collaboration** of monoliths
2. Offers the **autonomy and scalability** of microservices
3. Avoids **premature operational complexity**
4. Enables **gradual evolution** according to organizational maturity
We formalize this concept through **10 fundamental principles** empirically validated in enterprise projects at companies like Atlassian, BuildOps, and various high-growth startups.
---
## 2. State of the Art and Limitations
### 2.1 Traditional Monoliths
**Characteristics:**
- All code in a single codebase
- Single, atomic deployment
- Internal communication via function calls
- Shared state (monolithic database)
**Benefits:**
- Operational simplicity
- Native ACID transactions
- Facilitated debugging
- Lower network overhead
**Limitations:**
- Limited scalability (scale everything or nothing)
- High accidental coupling
- Risky changes (large blast radius)
- Difficulty in team parallelization
### 2.2 Microservices: Promises vs Reality
Microservices architecture was widely adopted with promises of:
- Individual scalability per service
- Failure isolation
- Independent release cycles
- Team autonomy
- Clear abstraction boundaries
### 2.3 Empirical Evidence of Problems
The article "Towards Modern Development of Cloud Applications" (Ghemawat et al., 2023), published at HotOS and developed by Google researchers, including Sanjay Ghemawat (co-creator of MapReduce and Google File System), identifies **five systemic problems** in microservices:
#### 2.3.1 Performance Loss
Data serialization and network calls add significant overhead compared to local calls. The more fragmented the system, the higher the communication cost.
#### 2.3.2 Loss of Visibility
With multiple versions of multiple services in production simultaneously, it becomes extremely difficult to predict emergent behaviors. Most critical failures occur in **interactions between different versions** of services.
#### 2.3.3 Management Difficulty
Each service becomes an independent binary with its own build, test, and deployment process. The effort to keep everything working together grows exponentially.
#### 2.3.4 API Freezing
When publishing an API between services, you freeze its evolution. Old APIs persist indefinitely because there's no visibility of who consumes them, creating cumulative technical debt.
#### 2.3.5 Development Slowdown
Changes affecting multiple services cannot be made atomically. Developers need to orchestrate changes across N services with misaligned versions, drastically reducing delivery velocity.
### 2.4 The Fundamental Error
According to Ghemawat et al. (2023), the error of microservices is in **conflating logical boundaries (code) with physical boundaries (deployment)**:
- Microservices force deployment decisions at code-writing time
- This locks the system: contracts and integration points become immutable
- Undoing separation or reorganizing becomes nearly impossible
### 2.5 Comparative Table: Granularity
| **Criteria** | **Coarse (Monolith)** | **Medium (Modular)** | **Fine (Microservices)** |
|---|---|---|---|
| **Operational Complexity** | Low | Medium | High |
| **Initial Velocity** | High | High | Low |
| **Scalability** | Limited | Flexible | Maximum |
| **System Visibility** | Total | High | Fragmented |
| **Deployment** | Single | Per app | Per service |
| **Coupling** | High | Controlled | Distributed |
| **Required Governance** | Low | Medium | High |
```mermaid
graph LR
A[Monolith
Complexity
Medium] --> B[Modular
Complexity
Minimal]
B --> C[Microservices
Complexity
High]
```
**Figure 1: Operational Complexity Sweet Spot**
*Modular Architectures occupy the point of minimal operational complexity, balancing the benefits of monoliths (simplicity) with those of microservices (autonomy), avoiding the problems of both extremes.*
### 2.6 Identified Gap
There is no formal framework that allows:
- Starting simple and evolving gradually
- Separating modularization decisions from deployment decisions
- Maintaining total visibility while offering autonomy
- Avoiding irreversible granularity commitments
**Modular Architectures** fill this gap.
---
## 3. Modular Architectures - Formal Definition
### 3.1 Definition
> **Modular Architecture** is a software system that establishes **explicit separation between logical boundaries (modules)** and **physical boundaries (processes/deployments)**, allowing granularity to evolve incrementally and reversibly according to business needs and organizational maturity.
### 3.2 Fundamental Characteristics
#### 3.2.1 Modularization in Monorepo
All code lives in a single versioned repository, organized into:
- **Apps**: bootstrap points that orchestrate modules
- **Packages**: logical units containing business domains or infrastructure
#### 3.2.2 Independent Deployment as an Option
Modules **can** be deployed together (modular monolith) or separately (dedicated apps), without requiring code refactoring. The deployment decision is **operational**, not **structural**.
#### 3.2.3 Gradual Evolution
The architecture supports natural transitions:
```
Modular Monolith → Modules with Clear Boundaries →
Dedicated Apps → Microservices (when justified)
```
### 3.3 Architectural Components
#### 3.3.1 Apps (Bootstrap Points)
Apps are executables that import and initialize modules. They contain no business logic.
```
apps/
monolith/ # Imports all modules
main.ts # Bootstrap
monolith.module.ts # Orchestration
billing-api/ # Imports only Billing
main.ts
billing-api.module.ts
```
#### 3.3.2 Packages (Logical Units)
**Domain Modules**: encapsulate business rules of a Bounded Context (DDD)
```
packages/
billing/
core/ # Entities, use cases
persistence/ # Repositories, migrations
http/ # Controllers, DTOs
__test__/ # Isolated tests
```
**Infrastructure Modules**: provide reusable technical capabilities
```
packages/
shared/
logger/
config/
database/
```
#### 3.3.3 Explicit Dependency Graph
Tools like Nx automatically build a graph showing how modules relate, enabling:
- Detection of circular dependencies
- Running tests only on affected modules (`nx affected`)
- Incremental builds
```
monorepo-root/
│
├── apps/ [Apps = Bootstrap]
│ ├── monolith/ ← Imports all modules
│ │ ├── main.ts
│ │ └── monolith.module.ts
│ │
│ ├── billing-api/ ← Only Billing
│ │ ├── main.ts
│ │ └── billing-api.module.ts
│ │
│ └── content-worker/ ← Only Content/VideoProcessor
│ ├── main.ts
│ └── content-worker.module.ts
│
├── packages/ [Packages = Logic]
│ │
│ ├── DOMAINS
│ │ │
│ │ ├── billing/ ← Bounded Context
│ │ │ ├── core/ (entities, use cases)
│ │ │ ├── persistence/ (repositories, migrations)
│ │ │ ├── http/ (controllers, DTOs)
│ │ │ └── __test__/
│ │ │
│ │ ├── content/
│ │ │ ├── catalog/ ← Submodule
│ │ │ ├── video-processor/ ← Submodule
│ │ │ └── admin/ ← Submodule
│ │ │
│ │ └── identity/
│ │ ├── core/
│ │ ├── persistence/
│ │ └── http/
│ │
│ └── INFRASTRUCTURE
│ └── shared/
│ ├── logger/ (no business logic)
│ ├── config/
│ └── database/
│
├── tools/
│ └── scripts/
│
├── nx.json [Nx Configuration]
├── package.json
└── tsconfig.base.json
```
### 3.4 Enabling Tooling
Modular Architectures are enabled by modern tools:
| **Technology** | **Capability** | **Examples** |
|---|---|---|
| **Monorepo Tools** | Dependency management, incremental builds | Nx, Bazel, Turborepo |
| **Modular Frameworks** | Composition via modules, dependency injection | NestJS, Spring Boot, .NET |
| **Native Modularization** | Module systems in languages | Java Modules, Go packages |
| **Integrated Solutions** | Flexible deployment maintaining modularity | Ktor, Maven Packages, Java Modulith |
### 3.5 Fundamental Differentiation
| **Aspect** | **Microservices** | **Modular Architectures** |
|---|---|---|
| **Deployment Decision** | Defined in architecture | Separated from architecture |
| **Granularity** | Fixed from the start | Evolves gradually |
| **Reversibility** | Difficult | Natural |
| **Initial Complexity** | High | Low |
| **Visibility** | Fragmented | Total (monorepo) |
```mermaid
graph TB
subgraph apps [Apps - Bootstrap]
monolith[monolith/]
billing_api[billing-api/]
content_worker[content-worker/]
end
subgraph packages [Packages - Logic]
subgraph domain [Domains]
billing[billing/]
content[content/]
identity[identity/]
end
subgraph infra [Infrastructure]
shared[shared/]
end
end
monolith --> billing
monolith --> content
monolith --> identity
monolith --> shared
billing_api --> billing
billing_api --> shared
content_worker --> content
content_worker --> shared
style monolith fill:#4A90E2
style billing_api fill:#4A90E2
style content_worker fill:#4A90E2
style billing fill:#50C878
style content fill:#50C878
style identity fill:#50C878
style shared fill:#9CA3AF
```
---
## 4. The 10 Fundamental Principles
This framework is structured into four categories of principles, each addressing a fundamental aspect of modularity.
### 4.1 Structural Principles
Define the **foundation of modular organization**.
#### 4.1.1 Well-Defined Boundaries
**Definition:** Each module has clear responsibilities aligned to a Bounded Context (DDD), without exposing internal details to other modules.
**Rationale:** Clear boundaries reduce accidental coupling, facilitate maintenance, and enable independent evolution. They follow the encapsulation principle extended to the architectural level.
**Implementation:**
The following examples show module setup in different languages. The concept applies to any technology - **modules can be implemented with or without frameworks**, using native language organization and encapsulation conventions.
**NestJS (TypeScript) - with framework:**
```typescript
// packages/billing/billing.module.ts
@Module({
imports: [TypeOrmModule.forFeature([SubscriptionEntity])],
providers: [BillingService],
controllers: [BillingController],
exports: [] // Nothing exported - communication via HTTP/events
})
export class BillingModule {}
```
**Go (without framework) - conventions only:**
```go
// packages/billing/module.go
package billing
// Module setup - public initialization function
func Setup(router *mux.Router, db *sql.DB) {
// Internal components (not exported)
repo := newRepository(db)
service := newService(repo)
// Only HTTP routes are exposed
setupRoutes(router, service)
}
```
**Ktor (Kotlin) - conventions + extension functions:**
```kotlin
// packages/billing/BillingModule.kt
package com.example.billing
fun Application.billingModule() {
// Internal components
val repository = BillingRepository(database)
val service = BillingService(repository)
// Only routes are exposed
routing {
billingRoutes(service)
}
}
```
**Universal principle:** The module is an **organizational unit** that:
- Groups related components (repositories, services, controllers)
- Exposes only public entry points (APIs, events)
- Keeps internal implementations private using language visibility
**Common violation:** Modules that export repositories or internal entities allow others to depend on implementation details.
#### 4.1.2 Composability
**Definition:** Modules are designed as reusable blocks that can be combined to form different applications or functionalities.
**Rationale:** Maximizes reuse, reduces duplication, and enables incremental evolution without rewriting code.
**Implementation:**
```typescript
// monolith.module.ts (imports everything)
@Module({
imports: [BillingModule, ContentModule, IdentityModule, LoggerModule]
})
export class MonolithModule {}
// billing-api.module.ts (imports only billing)
@Module({
imports: [BillingModule, LoggerModule]
})
export class BillingApiModule {}
```
**Benefit:** Same modules can be orchestrated in different ways without duplicating code.
#### 4.1.3 Independence
**Definition:** Modules are autonomous in code and infrastructure, communicating only via explicit APIs, events, or asynchronous messages.
**Rationale:** Prevents accidental coupling, enables independent technological evolution, and facilitates isolated testing.
**Implementation:**
- Each module has its own environment variables
- Communication via HTTP, gRPC, or messaging (never direct imports)
- Tests run without needing other modules
**Rule:** If module A needs data from module B, use API or event — never import classes from B directly.
### 4.2 Operational Principles
Define **how modules interact**.
#### 4.2.1 State Isolation
**Definition:** Each module manages its own state (database, cache, queues) without direct sharing with other modules.
**Rationale:** Prevents cascading failures, enables independent scalability, and facilitates schema evolution.
**Implementation:**
```mermaid
graph TB
subgraph "Billing Module"
BillingService[Billing Service]
BillingRepo[Billing Repository]
end
subgraph "Identity Module"
IdentityService[Identity Service]
IdentityRepo[Identity Repository]
end
subgraph "Database"
subgraph "Schema/DB Billing"
BillingTables["Tables:
• subscriptions
• payments
• invoices"]
end
subgraph "Schema/DB Identity"
IdentityTables["Tables:
• users
• sessions
• roles"]
end
end
BillingService --> BillingRepo
BillingRepo --> BillingTables
IdentityService --> IdentityRepo
IdentityRepo --> IdentityTables
BillingRepo -.->|❌ Never accesses| IdentityTables
IdentityRepo -.->|❌ Never accesses| BillingTables
style BillingService fill:#4A90E2
style IdentityService fill:#50C878
style BillingTables fill:#FFE5B4
style IdentityTables fill:#E0FFE0
style BillingRepo fill:#4A90E2
style IdentityRepo fill:#50C878
```
**Figure: State Isolation between Modules**
*Each module accesses exclusively its own tables, without direct state sharing. Communication between modules occurs only via APIs or events.*
**Database strategies:**
- **Shared Database with Separate Schemas**: isolated schemas or table prefixes (good for starting)
- **Completely Separate Databases**: total physical isolation (good for maturity)
#### 4.2.2 Explicit Communication
**Definition:** All communication between modules occurs via well-defined contracts (interfaces, DTOs, event schemas).
**Rationale:** Avoids implicit coupling, enables contract versioning, and facilitates testing with mocks.
**Implementation:**
**Local (same process):**
```typescript
// Public interface
export interface BillingAPI {
createSubscription(userId: string, planId: string): Promise;
}
// Implementation
@Injectable()
export class BillingService implements BillingAPI {
async createSubscription(...) { /* logic */ }
}
```
**Remote (separate processes):**
```typescript
@Controller('billing')
export class BillingController {
@Post('subscribe')
async createSubscription(@Body() dto: CreateSubscriptionDto) { /* ... */ }
}
```
**Asynchronous (events):**
```typescript
@OnEvent('billing.subscription.created')
handleSubscriptionCreated(payload: SubscriptionCreatedEvent) { /* ... */ }
```
#### 4.2.3 Replaceability
**Definition:** Modules can be removed, changed, or replaced without affecting the rest of the architecture.
**Rationale:** Enables technological experimentation, reduces vendor lock-in, and facilitates refactoring.
**Implementation:**
- Modules don't export concrete classes
- External dependencies are abstracted via interfaces
- Public contracts remain stable while implementations change
**Example:** Switch ORM (TypeORM → Prisma) within a module without impacting consumers.
### 4.3 Deployment Principles
Define **deployment flexibility**.
#### 4.3.1 Deployment Independence
**Definition:** Each app can be versioned and deployed in isolation, without forcing deployments of other apps.
**Rationale:** Reduces change risk, enables differentiated release cycles, and facilitates rollbacks.
**Implementation (Example):**
```yaml
# CI/CD Pipeline (GitHub Actions example)
name: Deploy Billing API
on:
push:
branches: [main]
jobs:
check-affected:
runs-on: ubuntu-latest
steps:
- run: npx nx affected:apps --base=origin/main
- if: contains(steps.affected.outputs.apps, 'billing-api')
run: npx nx build billing-api && npm run deploy:billing-api
```
**Strategy:** Use `nx affected` to detect changes and deploy only impacted apps.
#### 4.3.2 Individual Scale
**Definition:** Each app can scale horizontally according to its specific demand, without affecting others.
**Rationale:** Optimizes resource usage and prevents local bottlenecks from impacting the entire system.
**Strategy:** Metrics per app determine independent scaling.
### 4.4 Resilience Principles
Define **reliable production operation**.
#### 4.4.1 Observability
**Definition:** Each module has its own logs, metrics, and tracing, enabling isolated diagnosis.
**Rationale:** Identifies problems quickly without searching the entire system.
**Implementation:**
```typescript
// Logs with module context
logger.log('Subscription created', {
module: 'billing',
userId,
planId
});
// Metrics per module
@Metrics({ module: 'billing' })
export class BillingService { /* ... */ }
```
**Tooling:** Prometheus (metrics), Grafana (dashboards), Jaeger (distributed tracing).
#### 4.4.2 Fail Independence
**Definition:** Failures in one module don't propagate, ensuring controlled system degradation.
**Rationale:** Maximizes availability and facilitates recovery.
**Implementation:**
```typescript
// Circuit Breaker
import CircuitBreaker from 'opossum';
@Injectable()
export class BillingClient {
private breaker = new CircuitBreaker(
() => this.callBillingAPI(),
{ timeout: 5000, errorThresholdPercentage: 50 }
);
async getBillingData() {
try {
return await this.breaker.fire();
} catch (err) {
// Fallback
return { status: 'degraded', data: null };
}
}
}
```
**Strategies:** Circuit breakers, timeouts, retries with exponential backoff, fallbacks.
## The 10 Principles Organized
```mermaid
graph TB
subgraph resilience [Resilience]
P9[9. Observability]
P10[10. Fail Independence]
end
subgraph deploy [Deployment]
P7[7. Deployment Independence]
P8[8. Individual Scale]
end
subgraph operational [Operational]
P4[4. State Isolation]
P5[5. Explicit Communication]
P6[6. Replaceability]
end
subgraph structural [Structural]
P1[1. Well-Defined Boundaries]
P2[2. Composability]
P3[3. Independence]
end
P1 --> P4
P2 --> P5
P3 --> P6
P4 --> P7
P5 --> P8
P6 --> P7
P7 --> P9
P8 --> P10
style resilience fill:#EF4444,color:#fff
style deploy fill:#F97316,color:#fff
style operational fill:#3B82F6,color:#fff
style structural fill:#10B981,color:#fff
```
---
## 5. Implementation Patterns
### 5.1 Types of Modules
#### 5.1.1 Domain Modules
Represent Bounded Contexts (DDD) and encapsulate specific business rules.
**Examples:** `billing/`, `content/`, `identity/`
**Typical structure:**
```
billing/
├── core/ # Entities, services, use cases
├── persistence/ # Repositories, migrations
├── http/ # Controllers, DTOs
└── __test__/ # Unit and E2E tests
```
#### 5.1.2 Infrastructure Modules (Shared)
Provide reusable technical capabilities, without business logic.
**Examples:** `shared/logger/`, `shared/config/`, `shared/database/`
**Rule:** Never include business rules in shared. If two domains share logic, it indicates a poorly defined boundary.
### 5.2 Submodules
When domains become too large, divide them into **submodules** while maintaining Bounded Context cohesion.
**Example:**
```
content/
├── catalog/ # Submodule: catalog management
├── video-processor/ # Submodule: video processing
└── admin/ # Submodule: content administration
```
**Benefits:**
- Greater internal cohesion
- Controlled sharing within the domain
- Separate scaling (e.g., video-processor can run as isolated worker)
**Rule:** Submodules can share entities and repositories, but **never** services or application logic.
### 5.3 App Design Patterns
#### 5.3.1 Pattern 1: Shared App + Domain Apps
**Structure:**
```
apps/
monolith/ # Shared app (all modules)
billing-api/ # Dedicated app (only Billing)
content-worker/ # Dedicated app (only Content/VideoProcessor)
```
**When to use:**
- Small/medium companies
- Domains still being discovered
- Need for initial velocity
**Advantages:**
- Low operational complexity
- Easy to add new modules
- Enables gradual promotion to dedicated apps
**Challenges:**
- Shared on-call (requires alerts per module)
- Joint deployments (use feature flags)
#### 5.3.2 Pattern 2: Apps per Domain
**Structure:**
```
apps/
billing-app/ # Only Billing modules
content-app/ # Only Content modules
identity-app/ # Only Identity modules
```
**When to use:**
- Well-established domains
- Mature teams
- Need for strong isolation
**Advantages:**
- Maximum autonomy
- Independent pipelines
- Granular scaling
**Challenges:**
- Greater operational overhead
- Risk of excessive granularity
### 5.4 Evolution Strategy
**Phase 1: Modular Monolith**
- One shared app
- All modules together
- Defined logical boundaries
**Phase 2: Modules with Clear Boundaries**
- Still one shared app
- Strong governance (linters, enforce-module-boundaries)
- Observability per module
**Phase 3: Dedicated Apps**
- Critical modules promoted to their own apps
- Independent deployment and scaling
- Shared app still exists for smaller modules
**Phase 4: Extraction to Microservices**
- Only when necessary (compliance, extreme scale)
- Communication exclusively via network
- Justified operational cost
```mermaid
timeline
title Evolution of Modular Architectures
section Phase 1
Modular Monolith : 1 app
: All modules
: Maximum velocity
section Phase 2
Clear Boundaries : Enforce boundaries
: Observability per module
: Feature flags
section Phase 3
Dedicated Apps : 2+ apps
: Independent deployment
: Selective scaling
section Phase 4
Microservices : Many teams
: Network mandatory
: Maximum autonomy
```
---
## 6. Organizational Governance
### 6.1 Inverse Conway Architecture
Conway's Law establishes that systems reflect organizational structures. In Modular Architectures, we invert this: **code structure influences organization**.
Well-defined modules naturally create:
- Clear ownership
- Team autonomy
- Reduced organizational dependencies
### 6.2 Tech Lead Responsibilities
Technical leaders in modular architectures must:
1. **Establish Separation Guidelines**
- When to create a new module
- When to promote a module to a dedicated app
- Cohesion and coupling criteria
2. **Ensure Observability per Module**
- Dedicated dashboards
- Alerts directed to responsible teams
- Incident playbooks
3. **Balance Autonomy and Coherence**
- Autonomy: teams choose implementations
- Coherence: contracts and patterns are consistent
4. **Manage Architectural Evolution**
- Review dependency graph periodically
- Identify accidental coupling
- Facilitate boundary refactorings
### 6.3 Versioning: Fast Forward vs Time Machine
#### 6.3.1 Fast Forward (Monorepo)
- Everyone always on the latest version
- Zero "bump hell"
- Pipeline ensures consistency via `nx affected`
- **Ideal for velocity and consistency**
#### 6.3.2 Time Machine (Polyrepo)
- Each package with its own version
- Possibility of staying on old versions
- High synchronization cost
- **Generates maintenance complexity**
**Recommendation:** Fast Forward for evolutionary architectures.
### 6.4 Operational Challenges
#### 6.4.1 Shared On-Call
**Problem:** Who handles alerts when multiple teams share an app?
**Solution:**
- Alerts with module tag
- Automatic routing based on ownership
- Separate dashboards per domain
#### 6.4.2 Dependency Management
**Problem:** Modules can create circular or unwanted dependencies.
**Solution:**
```json
// nx.json - enforce-module-boundaries
{
"tags": {
"domain:billing": ["domain:billing", "shared"],
"domain:content": ["domain:content", "shared"],
"shared": []
}
}
```
Rule: Domains only depend on shared, never on each other.
#### 6.4.3 Feature Flags for Joint Deployments
**Problem:** Shared app deployments affect multiple teams.
**Solution:**
```typescript
if (featureFlags.isEnabled('billing-new-checkout', userId)) {
return newCheckoutFlow();
}
return legacyCheckoutFlow();
```
---
## 7. Comparative Analysis and Trade-offs
### 7.1 Detailed Comparative Table
| **Criteria** | **Monolith** | **Modular** | **Microservices** |
|---|---|---|---|
| **Operational Complexity** | Low | Medium | High |
| **Initial Velocity** | High | High | Low |
| **Scalability** | Vertical | Selective horizontal | Total horizontal |
| **Visibility** | Total | Total (monorepo) | Fragmented |
| **Deployment** | Single | Per app | Per service |
| **Coupling** | High | Controlled | Distributed |
| **Cognitive Load** | High (local) | Medium | High (global) |
| **Operational Cost** | Low | Medium | High |
| **Team Autonomy** | Low | Medium-High | High |
| **Reversibility** | Difficult | Easy | Very difficult |
### 7.2 When to Use Modular Architectures
**Yes, when:**
- Medium to large teams (5-100+ people)
- Domains in evolution or discovery
- Need for frequent collaboration between teams
- Mixed scaling requirements (some modules need to scale, others don't)
- Established monorepo culture
- Priority on delivery velocity
### 7.3 When NOT to Use
**Consider alternatives when:**
**Prefer Simple Monolith:**
- Very small system (1-3 developers)
- Extremely simple domain
- Rapid prototyping
**Prefer Microservices:**
- Totally stable and isolated domains
- Need for total physical isolation (compliance, regulatory)
- Completely independent teams geographically
- Extreme heterogeneous scaling requirements
### 7.4 Identified Limitations
1. **Requires Governance Discipline**
- Without enforcement, boundaries degrade
- Requires active technical leaders
2. **Specific Tooling**
- Nx, Bazel, or equivalent are necessary
- Initial learning curve
3. **Shared Database**
- If using single database, depends on discipline for isolation
- No physical guarantees of separation
4. **Monorepo Culture**
- Teams used to polyrepo may resist
- Requires cultural change
---
## 8. Related Work
### 8.1 Domain-Driven Design (Evans, 2003)
**Contribution:** Concept of **Bounded Contexts** as modeling units.
**Relation:** Modular Architectures implement Bounded Contexts as domain modules, with technical boundaries beyond conceptual ones.
**Reference:** Evans, E. (2003). *Domain-Driven Design: Tackling Complexity in the Heart of Software*. Addison-Wesley.
### 8.2 Towards Modern Development of Cloud Applications (Ghemawat et al., 2023)
**Contribution:** Empirical evidence of systemic problems in microservices.
**Relation:** Validates the need to separate logical from physical boundaries, conceptual basis of this work.
**Reference:** Ghemawat, S., et al. (2023). Towards Modern Development of Cloud Applications. *HotOS '23*.
### 8.3 Java Modulith (Spring Ecosystem)
**Contribution:** Modularity in the Java Spring Boot ecosystem.
**Relation:** Implements similar principles of Bounded Contexts in Java monorepos.
**Difference:** Specific to Java; this work generalizes to multiple languages.
### 8.4 Balancing Coupling in Software Design (Khononov, 2023)
**Contribution:** Analysis of global vs local complexity in distributed systems.
**Relation:** Substantiates the argument that modern complexity is in communication, not internal code.
**Reference:** Khononov, V. (2023). *Balancing Coupling in Software Design*. Addison-Wesley.
### 8.5 Nx Documentation (Nrwl)
**Contribution:** Monorepo practices and patterns focused on modularity.
**Relation:** Tooling that enables practical implementation of principles.
**Reference:** Nrwl. (2024). *Nx Documentation*. https://nx.dev
### 8.6 NestJS (Kamil Mysliwiec)
**Contribution:** Node.js framework with modular architecture inspired by Angular, promoting clear separation of responsibilities via modules, dependency injection, and decorators.
**Relation:** Practical example of framework that facilitates implementation of modular architectures, demonstrating how modular structures can be implemented natively in modern ecosystems.
**Reference:** Mysliwiec, K. et al. (2024). *NestJS - A progressive Node.js framework*. https://nestjs.com
### 8.7 Strategic Monoliths and Microservices (Vernon, 2021)
**Contribution:** Pragmatic analysis of when and how to use monoliths versus microservices, emphasizing purposeful architecture and the importance of architectural decisions aligned with business context.
**Relation:** Complements this work by recognizing that not every system needs microservices and that well-structured (modular) monoliths can be the most strategic choice. Vernon highlights the importance of Bounded Contexts and internal modularity, central concepts in our framework.
**Reference:** Vernon, V. (2021). *Strategic Monoliths and Microservices: Driving Innovation Using Purposeful Architecture*. Addison-Wesley Signature Series.
### 8.8 Enterprise Monorepo Angular Patterns (Nrwl/Nx Team)
**Contribution:** Practical patterns for organizing monorepos in enterprise environments, focusing on team scalability, code sharing, and dependency governance.
**Relation:** Validates in practice the monorepo modularization concepts presented in this work. Demonstrates how tools like Nx enable modular architectures in large-scale enterprise projects, with concrete examples of project structuring, libraries, and apps.
**Reference:** Nrwl. *Enterprise Monorepo Angular Patterns*. Nrwl Press.
### 8.9 Fundamentals of Software Architecture (Richards & Ford, 2020)
**Contribution:** Academic formalization of different architectural styles, including **modular monoliths** as a valid architectural pattern. Presents framework for analyzing trade-offs and architectural characteristics.
**Relation:** Provides theoretical basis for classifying modular architectures as a distinct architectural style. Validates the importance of characteristics like modularity, deployability, and testability that underpin our 10 principles.
**Reference:** Richards, M., & Ford, N. (2020). *Fundamentals of Software Architecture: An Engineering Approach*. O'Reilly Media.
### 8.10 Software Architecture: The Hard Parts (Ford, Richards et al., 2021)
**Contribution:** Deep analysis of trade-offs in modern distributed architectures, questioning when microservices really pay off and exploring alternatives like modular monoliths.
**Relation:** Complements this work by providing methodology for analyzing architectural trade-offs. Reinforces the message that architectural decisions should be based on context and real needs, not hype.
**Reference:** Ford, N., Richards, M., Sadalage, P., & Dehghani, Z. (2021). *Software Architecture: The Hard Parts: Modern Trade-Off Analyses for Distributed Architectures*. O'Reilly Media.
### 8.11 Building Evolutionary Architectures (Ford, Parsons, Kua, 2017)
**Contribution:** Concept of **fitness functions** and architectures that evolve incrementally over time, supporting constant change through engineering practices.
**Relation:** Directly substantiates the gradual evolution proposal of this framework. The concept of fitness functions can be applied to validate that modules maintain their boundaries (via enforce-module-boundaries) and that principles are respected over time.
**Reference:** Ford, N., Parsons, R., & Kua, P. (2017). *Building Evolutionary Architectures: Support Constant Change*. O'Reilly Media.
### 8.12 Team Topologies (Skelton & Pais, 2019)
**Contribution:** Framework for structuring technology teams based on workflow, defining patterns like stream-aligned teams, enabling teams, and platform teams.
**Relation:** Essential for the Organizational Governance section of this work. Modular architectures naturally align with stream-aligned teams, where each team owns one or more domain modules, with platform teams providing infrastructure modules (shared).
**Reference:** Skelton, M., & Pais, M. (2019). *Team Topologies: Organizing Business and Technology Teams for Fast Flow*. IT Revolution Press.
### 8.13 Monolith to Microservices (Newman, 2019)
**Contribution:** Pragmatic guide on monolith decomposition, recognizing that not all systems should become microservices and presenting patterns for incremental transition when necessary.
**Relation:** Validates the gradual evolution approach proposed in this framework. Newman demonstrates that transition should be conscious and incremental, exactly what modular architectures facilitate by establishing clear boundaries from the start.
**Reference:** Newman, S. (2019). *Monolith to Microservices: Evolutionary Patterns to Transform Your Monolith*. O'Reilly Media.
### 8.14 Differentiation of This Work
This paper goes beyond by:
1. **Formalizing 10 principles** as a cohesive framework
2. **Proposing implementation patterns** empirically validated
3. **Including organizational governance** as an integral part
4. **Establishing gradual evolution strategy** between paradigms
---
## 9. Conclusion
### 9.1 Synthesis
Modular Architectures represent a **"conscious middle ground"** between traditional monoliths and microservices, grounded in:
- **Explicit separation** between logical and physical boundaries
- **Gradual evolution** of granularity according to maturity
- **Total visibility** via monorepo while maintaining autonomy
- **Controlled complexity** avoiding premature overhead
### 9.2 Main Contributions
This work offers:
1. **Formal Framework with 10 Principles**
- Structural: Boundaries, Composability, Independence
- Operational: State Isolation, Communication, Replaceability
- Deployment: Deployment Independence, Individual Scale
- Resilience: Observability, Fail Independence
2. **Validated Implementation Patterns**
- Module types (domain vs infrastructure)
- Submodules for large domains
- App design patterns (shared vs dedicated)
3. **Organizational Governance Model**
- Tech lead responsibilities
- Fast Forward versioning
- Management of operational challenges
4. **Gradual Evolution Strategy**
- 4 phases: Modular Monolith → Clear Boundaries → Dedicated Apps → Microservices
- Reversible decisions at each phase
### 9.3 Positioning
Modular Architectures **are not revolution**, but **natural evolution** of:
- Domain-Driven Design (concepts)
- Modern tooling (technical enablement)
- Empirical evidence (scientific validation)
### 9.4 Future Work
Research directions:
1. **Quantitative Metrics of Cognitive Load**
- Formalize metrics to measure perceived complexity
- Compare with monoliths and microservices
2. **Longitudinal Case Studies**
- Track evolution of real systems over years
- Validate technical debt reduction hypothesis
3. **Validation in Different Ecosystems**
- Java, Go, .NET, Python
- Identify universal vs language-specific patterns
4. **Automated Governance Tooling**
- Static analysis of principle violations
- Automated refactoring suggestions
### 9.5 Openness
This work is an **open contribution to the community**. We encourage:
- Feedback and constructive criticism
- Implementations in different contexts
- Empirical validation in new domains
- Extensions and refinements of the framework
---
## References
1. Evans, E. (2003). *Domain-Driven Design: Tackling Complexity in the Heart of Software*. Addison-Wesley Professional.
2. Ghemawat, S., Grandl, R., Petrovic, S., Schwarzkopf, M., Whittaker, M., & McKinley, K. S. (2023). Towards Modern Development of Cloud Applications. In *Proceedings of the 19th Workshop on Hot Topics in Operating Systems (HotOS '23)*. ACM. https://doi.org/10.1145/3593856.3595909
3. Khononov, V. (2023). *Balancing Coupling in Software Design: Successful Software Architecture in General and Distributed Systems in Particular*. Addison-Wesley Professional.
4. Newman, S. (2021). *Building Microservices: Designing Fine-Grained Systems* (2nd ed.). O'Reilly Media.
5. Nrwl. (2024). *Nx: Smart Monorepos · Fast CI*. Retrieved from https://nx.dev
6. Mysliwiec, K. et al. (2024). *NestJS: A progressive Node.js framework*. Retrieved from https://nestjs.com
7. Google. (2023). *Service Weaver: A Framework for Writing Distributed Applications*. Retrieved from https://serviceweaver.dev
8. Spring. (2024). *Modulith: A Modular Monolith Toolkit for Spring Boot*. Retrieved from https://spring.io/projects/spring-modulith
9. Fowler, M. (2014). *Microservices: A Definition of This New Architectural Term*. Retrieved from https://martinfowler.com/articles/microservices.html
10. Vernon, V. (2013). *Implementing Domain-Driven Design*. Addison-Wesley Professional.
11. Vernon, V. (2021). *Strategic Monoliths and Microservices: Driving Innovation Using Purposeful Architecture*. Addison-Wesley Signature Series.
12. Nrwl. *Enterprise Monorepo Angular Patterns*. Nrwl Press.
13. Richards, M., & Ford, N. (2020). *Fundamentals of Software Architecture: An Engineering Approach*. O'Reilly Media.
14. Ford, N., Richards, M., Sadalage, P., & Dehghani, Z. (2021). *Software Architecture: The Hard Parts: Modern Trade-Off Analyses for Distributed Architectures*. O'Reilly Media.
15. Ford, N., Parsons, R., & Kua, P. (2017). *Building Evolutionary Architectures: Support Constant Change*. O'Reilly Media.
16. Skelton, M., & Pais, M. (2019). *Team Topologies: Organizing Business and Technology Teams for Fast Flow*. IT Revolution Press.
17. Newman, S. (2019). *Monolith to Microservices: Evolutionary Patterns to Transform Your Monolith*. O'Reilly Media.
18. Richardson, C. (2018). *Microservices Patterns: With Examples in Java*. Manning Publications.
---
## About the Authors
**Waldemar Neto** is co-founder of TechLeads.club and has experience in software architecture at companies like ThoughtWorks, Atlassian, and high-growth startups. Specialist in evolutionary systems and software engineering practices.
- LinkedIn: https://www.linkedin.com/in/waldemarnt/
**William Calderipe** is co-founder of TechLeads.club with experience in enterprise development at Atlassian and other large-scale organizations. Focus on scalable architectures and DevOps practices.
- LinkedIn: https://www.linkedin.com/in/wcalderipe/
---
**Contact:**
- Website: https://techleads.club
- Email: waldemarnt@gmail.com
- LinkedIn Waldemar Neto: https://www.linkedin.com/in/waldemarnt/
- LinkedIn William Calderipe: https://www.linkedin.com/in/wcalderipe/
**License:** This work is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0).
You are free to:
- ✅ Share — copy and redistribute the material in any medium or format
- ✅ Adapt — remix, transform, and build upon the material for any purpose, even commercially
Under the following terms:
- 📝 Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made
More information: https://creativecommons.org/licenses/by/4.0/
**How to cite this work:**
```
Neto, W., & Calderipe, W. (2025). Modular Architectures: 10 Principles for
Building Evolutionary Systems. TechLeads.club Whitepaper, v1.0.
```
---
**Version:** 1.0
**Publication Date:** October 13, 2025
**Status:** Initial Publication — Feedback Welcome